Randomizing Model Based on Rarity.Value?

  1. What do you want to achieve?

I’d like the items to be randomized based on their Rarity.Value (NumberValue) (1 being most common to 4 being extremely rare) instead of just selecting random items from the Shop folder in ReplicatedStorage.

  1. What is the issue?

Currently, the script just selects a random item within the Shop folder without checking it’s Rarity.Value

  1. What solutions have you tried so far?

I’ve tried re-rewriting my script many many time but I’m back at square one…

ServerScript that randomizes the items (in ServerScriptService)

local replicatedStorage = game:GetService("ReplicatedStorage")
local shopFolder = replicatedStorage:WaitForChild("Shop")
local workspace = game:GetService("Workspace")

-- Find all the SpecialDisplay1 objects in the workspace
local specialDisplays = workspace:GetDescendants()
for _, obj in ipairs(specialDisplays) do
	if obj.Name == "SpecialDisplay" then
		-- Find all the ObjectDisplay children of each SpecialDisplay1 object
		local objectDisplays = obj:GetDescendants()
		for _, child in ipairs(objectDisplays) do
			if child.Name == "ObjectDisplay" then
				-- Spawn a random model for each ObjectDisplay child
				local function spawnRandomModel()
					local models = shopFolder:GetChildren()
					if #models == 0 then
						print("There are no models in the shop folder.")
						return
					end

					local randomModelIndex = math.random(1, #models)
					local randomModel = models[randomModelIndex]
					local clonedModel = randomModel:Clone()

					-- Find the root part of the cloned model
					local rootPart = clonedModel.PrimaryPart.CFrame.Position

					-- Calculate the offset between the root part and the ObjectDisplay child
					local offset = child.Position - rootPart

					-- Parent the cloned model to the ObjectDisplay child and move it to the correct position
					clonedModel.Parent = child
					clonedModel:SetPrimaryPartCFrame(CFrame.new(child.Position)) --+ offset))
				end

				-- Spawn a random model immediately for each ObjectDisplay child
				spawnRandomModel()

				-- Spawn a new random model for each ObjectDisplay child every 30 minutes
				--while true do
			--		wait(30 * 60)
			--		spawnRandomModel()
			--	end
			end
		end
	end
end

ClientScript within the GUI that finds the model within the ObjectDisplay part and makes it correspond to the purchase GUI

-- Get the ObjectDisplay part from the SpecialDisplay1 in the workspace that has Num.Value == 1
local objDisplay
for _, specialDisplay in ipairs(game.Workspace:GetDescendants()) do
	if specialDisplay.Name == "SpecialDisplay" then --specialDisplay:IsA("Folder") and
		local numValue = specialDisplay:FindFirstChild("Num")
		if numValue and numValue:IsA("NumberValue") and numValue.Value == 1 then
			objDisplay = specialDisplay:FindFirstChild("ObjectDisplay")
			break
		end
	end
end

--if objDisplay.Parent.Num.Value == 1 then 
--	correctstand = true
--end


-- Loop through each slot in the grid
for i, slot in ipairs(script.Parent.Slots:GetChildren()) do
	-- Check if the slot is a Frame object
	if slot:IsA("Frame") then
		-- Add a MouseEnter event listener to the slot
		slot.MouseEnter:Connect(function()
			
			-- Clone the first child and set its parent to the ViewportFrame.Model property of the specific slot
			local firstChild = objDisplay:GetChildren()[1]
			print(firstChild) -- check the value of firstChild
		
			-- Get the first child of the ObjectDisplay part and set the ItemName object in the Info variable to its Name property
			Info.ItemName.Text = firstChild.Name
			Info.ItemDescription.Text = firstChild:FindFirstChild("Desc").Value
			script.Parent.Slots.Cost.Text = "Coins: ".. firstChild:FindFirstChild("Cost").Value --added

			-- Clone the first child and set its parent to the ViewportFrame.Model property of the specific slot
			local clonedChild = objDisplay:GetChildren()[1]:Clone()
			clonedChild.Parent = slot.ViewportFrame.Model

			-- Set the Visible property of the Info object to true
			Info.Visible = true
		end)
		
		
		-- Add a MouseLeave event listener to the slot
		slot.MouseLeave:Connect(function()
			-- Set the Visible property of the Info object to false
			Info.Visible = false
		end)
		local slotframe = script.Parent.Slots

		local function update(specific)

			-- Disable and re-enable the RotateVis script under the specific slot's parent
			local rotateVis = slotframe:FindFirstChild(specific).RotateVis
			rotateVis.Disabled = true
			rotateVis.Disabled = false
		end

		--update()
		
		local slots = script.Parent.Slots
		--// glowing selection reset
		slots.Primary.ImageButton.Transparency =1
		
		local equipped = true
		
		for i, slot in ipairs(script.Parent.Slots:GetChildren()) do

			-- Check if the slot is a Frame object
			if slot:IsA("Frame") then
				
	
				-- Add a MouseClick event listener to the slot
				slot.ImageButton.MouseButton1Click:Connect(function(player)
					-- Get the first child of the ViewportFrame.Model and set the itemName variable to its Name property
					local itemModel = slot.ViewportFrame.Model:GetChildren()[1]
					local itemName = itemModel.Name

					local player = game.Players.LocalPlayer
					print(player) -- This will print the value of player in the console
					print(player.leaderstats)

					-- Check if the slot has an item equipped
					local shopitem = objDisplay:GetChildren()[1]
					local itemcost = shopitem:FindFirstChild("Cost").Value

					if equipped == true and itemcost > player.leaderstats:FindFirstChild("Coins").Value then

						player.PlayerGui.GameInventory.DeleteOneMsg.Visible = true
						player.PlayerGui.GameInventory.DeleteOneMsg.Msg.Text = "You don't have enough coins."
						--player.PlayerGui.GameInventory.Frame.Visible = true
						wait(2)
						player.PlayerGui.GameInventory.DeleteOneMsg.Visible = false

					else

						if equipped == true and itemcost <= player.leaderstats:FindFirstChild("Coins").Value then

							local event = game.ReplicatedStorage.InvEvents.PickUp
							local purchaseitem = game.ReplicatedStorage.ShopEvents.Purchase	
							local item = itemName
							local firstChild = itemModel
							local itemdescription = firstChild:FindFirstChild("Desc").Value
							event:FireServer(item, itemdescription)
							purchaseitem:FireServer(itemcost)	

							-- Set the Equipped value of the item to false and hide the item in the slot
							equipped = false
							slot.Visible = false


							-- Clear the model of the slot to remove the item from the slot
							slot.ViewportFrame.Model:ClearAllChildren()

							-- Update the value in the settings for the slot
							if slot == slotframe.Primary then
								script.Parent.Settings.Primary.Value = ''
							end
						end
						-- Play a sound
						playSound()

						--	script.Parent.Slots.Visible = false
						script.Parent.Parent.Parent:Destroy()
					end
				end)
			end
		end
	end
end

Ignore the - - ed out lines, that’s from me trying to figure things out :slight_smile:

1 Like

Did you try to assign a value to the objects with a table? If not then you can do that and use math.random to push forward with it

1 Like
local Objects = {
	object1 = {ObjectName,rarity},
	object2 = {ObjectName, rarity},
	object3 = {ObjectName,rarity},

}
1 Like

I assigned a Rarity.Value to the objects using a NumberValue object within each model named “Rarity”. I’m still a bit new to scripting so I’m not really sure how to go about creating a table :sweat_smile:

EDIT: Thank you for adding an example!

1 Like

This is just a possible snippet for you to look at

1 Like

I have 2 questions:

  1. Do I still need to have NumberValue objects in the models?

  2. Would my table look somewhat like this?

local Items = {
	object1 = {"Crate",1},
	object2 = {"Stealth", 3},
	object3 = {"Loot Finder" ,4},

}
1 Like

Sorry I realized I made a mistake in the code I made.

local Items = {
	objectName = {rarity},
	objectName = {rarity},       -- you can get the rarity by doing Items[name][1]  
	objectName = {rarity},

}
1 Like

Am I doing this right?

local replicatedStorage = game:GetService("ReplicatedStorage")
local shopFolder = replicatedStorage:WaitForChild("Shop")
local workspace = game:GetService("Workspace")

-- Define the items and their rarities
local Items = {
    Crate = 1,
    Stealth = 3,
    ["Loot Finder"] = 4,
}

-- Find all the SpecialDisplay1 objects in the workspace
local specialDisplays = workspace:GetDescendants()
for _, obj in ipairs(specialDisplays) do
    if obj.Name == "SpecialDisplay" then
        -- Find all the ObjectDisplay children of each SpecialDisplay1 object
        local objectDisplays = obj:GetDescendants()
        for _, child in ipairs(objectDisplays) do
            if child.Name == "ObjectDisplay" then
                -- Spawn a random model for each ObjectDisplay child
                local function spawnRandomModel()
                    local models = {}
                    for name, rarity in pairs(Items) do
                        if rarity >= math.random(1, 4) then
                            local model = shopFolder:FindFirstChild(name)
                            if model then
                                table.insert(models, model)
                            end
                        end
                    end
                    
                    if #models == 0 then
                        print("There are no models in the shop folder with the required rarity.")
                        return
                    end

                    local randomModelIndex = math.random(1, #models)
                    local randomModel = models[randomModelIndex]
                    local clonedModel = randomModel:Clone()

                    -- Find the root part of the cloned model
                    local rootPart = clonedModel.PrimaryPart.CFrame.Position

                    -- Calculate the offset between the root part and the ObjectDisplay child
                    local offset = child.Position - rootPart

                    -- Parent the cloned model to the ObjectDisplay child and move it to the correct position
                    clonedModel.Parent = child
                    clonedModel:SetPrimaryPartCFrame(CFrame.new(child.Position)) --+ offset))
                end

                -- Spawn a random model immediately for each ObjectDisplay child
                spawnRandomModel()

                -- Spawn a new random model for each ObjectDisplay child every 30 minutes
                --while true do
            --      wait(30 * 60)
            --      spawnRandomModel()
            --  end
            end
        end
    end
end

I don’t know about the space… but yes I believe so. (The space in [“Loot Finder”]

1 Like

“Loot Finder” is the exact name of the model in Shop folder — can I not have spaces in the object names?

1 Like

I don’t know all that well if you can have spaces in the names I just never tried it putting space in it

1 Like

An issue to the randomizing could also be how you use math.random

1 Like

What this does’

math.random(1, #models)

It doesn’t do anything with the rarity of the object, instead, it’s getting the number of objects you have in total for example lets say you have 10 models, it’s going to be a 1/10 chance for everything

1 Like

I see! What should I do instead for the math.random? I don’t have much experience with math.random; hence why I’m struggling with this.

1 Like

There’s a few ways you can do this, it depends if there will be new models etc.

1 Like

Thank you!

Here’s something I came up with:

local replicatedStorage = game:GetService("ReplicatedStorage")
local shopFolder = replicatedStorage:WaitForChild("Shop")
local workspace = game:GetService("Workspace")

-- Define the items and their rarities
local Items = {
	Crate = 1,
	["Beast Sense II"] = 3,
	["Soul Seeker III"] = 4,
}

-- Split the items into separate lists based on rarity
local commonItems = {}
local uncommonItems = {}
local rareItems = {}
local ultraRareItems = {}

for item, rarity in pairs(Items) do
	if rarity == 1 then
		table.insert(commonItems, item)
	elseif rarity == 2 then
		table.insert(uncommonItems, item)
	elseif rarity == 3 then
		table.insert(rareItems, item)
	elseif rarity == 4 then
		table.insert(ultraRareItems, item)
	end
end

-- Find all the SpecialDisplay1 objects in the workspace
local specialDisplays = workspace:GetDescendants()
for _, obj in ipairs(specialDisplays) do
	if obj.Name == "SpecialDisplay" then
		-- Find all the ObjectDisplay children of each SpecialDisplay1 object
		local objectDisplays = obj:GetDescendants()
		for _, child in ipairs(objectDisplays) do
			if child.Name == "ObjectDisplay" then
				-- Spawn a random model for each ObjectDisplay child
				local function spawnRandomModel()
					-- Select a random rarity level first
					local rarityLevel = math.random(1, 4)

					-- Select a random item from the list of items for the selected rarity level
					local itemsList = commonItems
					if rarityLevel == 2 then
						itemsList = uncommonItems
					elseif rarityLevel == 3 then
						itemsList = rareItems
					elseif rarityLevel == 4 then
						itemsList = ultraRareItems
					end

					local numItems = #itemsList
					if numItems == 0 then
						print("There are no items with rarity level "..rarityLevel..".")
						return
					end

					local randomItemIndex = math.random(1, numItems)
					local randomItemName = itemsList[randomItemIndex]

					local randomModel = shopFolder:FindFirstChild(randomItemName)
					if randomModel == nil then
						print("Model not found for item "..randomItemName..".")
						return
					end
					
					local clonedModel = randomModel:Clone()

					-- Find the root part of the cloned model
					local rootPart = clonedModel.PrimaryPart.CFrame.Position

					-- Calculate the offset between the root part and the ObjectDisplay child
					local offset = child.Position - rootPart

					-- Parent the cloned model to the ObjectDisplay child and move it to the correct position
					clonedModel.Parent = child
					clonedModel:SetPrimaryPartCFrame(CFrame.new(child.Position)) --+ offset))
				end

				-- Spawn a random model immediately for each ObjectDisplay child
				spawnRandomModel()

				-- Spawn a new random model for each ObjectDisplay child every 30 minutes
				--while true do
				--      wait(30 * 60)
				--      spawnRandomModel()
				--  end
			end
		end
	end
end

Does this seem to be a better way of using math.random?

Yes there is a better way what you have still doesn’t have rarity really in it
instead of that try something like this

local num = math.random(min,max)
if num >50then
        run
elseif num == 50 then 
run
elseif num <50 then
run
end

sorry for the bad format this snippet is a 1/100 chance for the 2nd elseif

1 Like

I won’t go through the entire function but it’s best practice to have your rarities add up to 100(%) so it’s easier to control exactly how “rare” something should be…

local Items = {
	Crate = 66,
	Stealth = 33,
	["Loot Finder"] = 1,
}

local Seed = Random.new(tick())

local function PickItem(RarityList: {[string]: number}): string
	local Num = Seed:NextNumber(0, 100)
	local Int = 0
	for Item, Chance in pairs(Items) do
		Int += Chance
		if Num <= Int then
			return  Item
		end
	end
end

local ChosenItem = PickItem(Items)
print(ChosenItem) -- 66% chance to be Crate, 33% chance to be Stealth, 1% chance to be Loot Finder
2 Likes

Thank you!!! This was extremely helpful; however, I’m struggling to figure out how to implement what you’ve given to me within the script I currently have written?

Is this how I would do it?

local replicatedStorage = game:GetService("ReplicatedStorage")
local shopFolder = replicatedStorage:WaitForChild("Shop")
local workspace = game:GetService("Workspace")

-- Define the items and their rarities
local Items = {
	Crate = 30,
	["Beast Sense I"] = 15,
	["Soul Seeker I"] = 15,
	["Loot Finder I"] = 20,
	["Stealth I"] = 20,
	["Beast Sense II"] = 10,
	["Soul Seeker II"] = 10,
	["Loot Finder II"] = 10,
	["Stealth II"] = 10,
	["Soul Seeker III"] = 5,
	["Loot Finder III"] = 5,
	["Beast Sense III"] = 5,
	["Stealth III"] = 5,
}

-- Find all the SpecialDisplay1 objects in the workspace
local specialDisplays = workspace:GetDescendants()
for _, obj in ipairs(specialDisplays) do
	if obj.Name == "SpecialDisplay" then
		-- Find all the ObjectDisplay children of each SpecialDisplay1 object
		local objectDisplays = obj:GetDescendants()
		for _, child in ipairs(objectDisplays) do
			if child.Name == "ObjectDisplay" then
				-- Spawn a random model for each ObjectDisplay child
				local Seed = Random.new(tick())

				local function PickItem(RarityList: {[string]: number}): string
					local Num = Seed:NextNumber(0, 100)
					local Int = 0
					for Item, Chance in pairs(Items) do
						Int += Chance
						if Num <= Int then
							return  Item
						end
					end
				end

				local ChosenItem = PickItem(Items)
				print(ChosenItem) -- 66% chance to be Crate, 33% chance to be Stealth, 1% chance to be Loot Finder
					
					local clonedModel = ChosenItem:Clone()

					-- Find the root part of the cloned model
					local rootPart = clonedModel.PrimaryPart.CFrame.Position

					-- Calculate the offset between the root part and the ObjectDisplay child
					local offset = child.Position - rootPart

					-- Parent the cloned model to the ObjectDisplay child and move it to the correct position
					clonedModel.Parent = child
					clonedModel:SetPrimaryPartCFrame(CFrame.new(child.Position)) --+ offset))
				end

				-- Spawn a random model immediately for each ObjectDisplay child
				--PickItem()

				-- Spawn a new random model for each ObjectDisplay child every 30 minutes
				--while true do
				--      wait(30 * 60)
				--      spawnRandomModel()
				--  end
			--end
		end
	end
end

EDIT: This didn’t work ._.