The value of leaderstats is "nil", but why?

What I am Trying to Achieve
I am creating a tycoon based game, so my goal is to make it so that when a player touches a button it takes the amount of (Cash - Cost) setting their new total in “leaderstats”.

The Issue
The current issue is when I try to use this setup I am receiving a print statement in output that there is not enough money although there is, as 0 is >= 0. I printed out some values and realized it was never making it to the function where it calculates the new amount of cash so my guess is the issue is somewhere within my first code I list below.
I currently am receiving the error “nil” when printing the value of leaderstats.
FYI: I printed it to try and narrow down what was going wrong, not as a part of the actual code

Video

Video:

Screenshots

error_LI

What I’ve Tried
So far I have been looking for a solution to what seems to be a syntax error, but now I’m not so sure. I have printed out the values of my cash, cost, and many other things and have narrowed the error line of code to line 30, but still have no clue what’s wrong with it.

Code:

local Players = game:GetService("Players")

--- Leaderstats ---

local function LeaderboardSetup()
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	
	local Cash = Instance.new("IntValue")
	Cash.Name = "Cash"
	Cash.Value = 1
	Cash.Parent = leaderstats
	return leaderstats
end

local PlayerManager = {}

--- Hooks Events to Proper Functions ---

function PlayerManager.Start()
	Players.PlayerAdded:Connect(PlayerManager.OnPlayerAdded)
end

function PlayerManager.OnPlayerAdded(player)
	local leaderstats = LeaderboardSetup()
	leaderstats.Parent = player
end

function PlayerManager.GetMoney(player)
	local leaderstats = player:FindFirstChild("leaderstats") 
--- Error: leaderstats is nil here
	print(leaderstats)
	if leaderstats then
		local Cash = leaderstats:FindFirstChild("Cash")
		print(Cash)
		if Cash then
			return Cash.Value
		end
	end
	return 0
end

function PlayerManager.SetMoney(player, value)
	if value then
		local leaderstats = player:FindFirstChild("leaderstats")
		if leaderstats then
			local Cash = leaderstats:FindFirstChild("Cash")
			if Cash then
				Cash.Value = value
			end
		end
	end
end

return PlayerManager

Error Code:

	local leaderstats = player:FindFirstChild("leaderstats")   --- Error Line 30

Code for getting new value of Cash

function Button:Press(player)
	local id = self.Instance:GetAttribute("Id")
	local cost = self.Instance:GetAttribute("Cost")
	local cash = PlayerManager.GetMoney(player)
	
	if player == self.Tycoon.Owner and cash >= cost then
		PlayerManager.SetMoney(player, cash - cost)
		self.Tycoon:PublishTopic("Button", id)
		self.Instance:Destroy()
	else
		print("not enough money")
	end
end

Thank you to anyone who takes the time to think about this one. If you need to be provided with any other information about my code please let me know. Just looking for some fresh eyes on something I am probably missing in my code.

“leaderstats” doesn’t exist when attempting to get it. You probably forgot to call :Start.

Anyways, I changed the code a little. What I did was:

  • Removed checks for an object existing. “leaderstats”, as well as all objects underneath it, should always exist. Getting an error faster helps you fix things faster.
  • Added an initialization function to the bottom of the module.
local PlayerManager = {}
local Players = game:GetService("Players")

--- Leaderstats ---

local function createLeaderstats(player: Player)
	-- Leaderstats folder
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	
	-- The values
	local Cash = Instance.new("IntValue")
	Cash.Name = "Cash"
	Cash.Value = 1
	Cash.Parent = leaderstats

	-- Finally, set the parent of the leaderstats folder
	leaderstats.Parent = player
end


--- Hooks Events to Proper Functions ---

function PlayerManager.Start()
	Players.PlayerAdded:Connect(PlayerManager.OnPlayerAdded)
end

function PlayerManager.OnPlayerAdded(player)
	createLeaderstats(player)
end

function PlayerManager.GetMoney(player)
	local leaderstats = player.leaderstats
	local Cash = leaderstats.Cash
	return Cash.Value
end

function PlayerManager.SetMoney(player, value)
	local leaderstats = player.leaderstats
	local Cash = leaderstats.Cash
	Cash.Value = value
end


local initialized = false
local function init()
	if not initialized then
		-- You can add any initialization code you want here
		-- A potential issue may arise when the player has joined before the event connected
		for _, player in ipairs(Players:GetPlayers()) do
			createLeaderstats(player)
		end

		-- We'll do this to make sure that the method is always called
		PlayerManager.Start()
	end

	return PlayerManager
end
return init()

i think leaderstats work fine but there is something must wrong with Button:Press function can we see code that call this function?

Yes, here is the code for button. Currently looking into the first response as well.

local PlayerManager = require(script.Parent.Parent.PlayerManager)

local Button = {}
Button.__index = Button

function Button.new(tycoon, part)
	local self = setmetatable({}, Button)
	self.Tycoon = tycoon
	self.Instance = part
	
	return self
end

--- Player Touches Button ---

function Button:Init()
	self.Prompt = self:CreatePrompt()
	self.Instance.Touched:Connect(function(...)
		print("touched")
		self:Press(...)
	end)
end

function Button:CreatePrompt()
	self.Instance.BillboardGui.TextLabel.Text = "$" .. self.Instance:GetAttribute("Cost")
end

--- Get's Rid of Button and Spawns The Part the Player Bought ---

function Button:Press(player)
	PlayerManager.Start()
	local id = self.Instance:GetAttribute("Id")
	local cost = self.Instance:GetAttribute("Cost")
	local cash = PlayerManager.GetMoney(player)
	
	if player == self.Tycoon.Owner and cash >= cost then
		PlayerManager.SetMoney(player, cash - cost)
		self.Tycoon:PublishTopic("Button", id)
		self.Instance:Destroy()
	else
		print("not enough money")
	end
end

return Button

I tried implementing this code to see how it works and I am now getting a new error. For some reason it is acting like it is searching for leaderstats in the players body parts.
error 2
error3

What is the player? Try to print it and it’s type and show what comes?

You aren’t parenting the leader stats folder to the Player

Always use FindFirstChild when referencing instances that may not be there to avoid errors.

2 Likes

The ... that passing with Touched event is a character hit part not player instance so change that function to

function Button:Init()
	self.Prompt = self:CreatePrompt()
	self.Instance.Touched:Connect(function(hitPart)
		if hitPart.Parent:FindFirstChild("Humanoid") then
			local Player = game:GetService("Players"):GetPlayerFromCharacter(hitPart.Parent)
			self:Press(Player)
		end
	end)
end
2 Likes

That would make sense. I’ll try that.

I just realized something that connects to what you said and could solve the issue, so I’ll get back to you.

Yep, you were correct. I was programming it as if it was a prompt, but it was a button. So the mixture of the two made my code faulty. It now works exactly like what I was looking for with minimum change. Thanks so much! I really appreciate it!

1 Like