Value returning as nil when assigning to metatable from localscript

Hi!

Pretty simple problem, I can’t figure out why this value (self.equipped) is returning as nil. It shouldn’t be as far as I can tell.

This is the LocalScript which hands off self.equipped to the module:

local StarterPlayer = game:GetService("StarterPlayer")

local PlayerModule = StarterPlayer:WaitForChild("StarterPlayerScripts"):WaitForChild("PlayerModule")
local ControlModule = PlayerModule:WaitForChild("ControlModule")

local WeaponSystemClientside = require(ControlModule.WeaponSystemClientside)

local tool = script.Parent
local configuration = tool.Configuration

--// These are separate for organisation purposes \\--

local function handOffVariables()
	WeaponSystemClientside.ammoCapacity = configuration.AmmoCapacity
	WeaponSystemClientside.currentAmmo = configuration.CurrentAmmo
	WeaponSystemClientside.magCapacity = configuration.MagCapacity
	WeaponSystemClientside.currentMag = configuration.CurrentMag
	WeaponSystemClientside.shotCooldown = configuration.ShotCooldown
end

-- This is to catch if something goes wrong when changing weapons for testing purposes
-- probably doesnt need to exist when script is finished. Idk
local function deleteVariables()
	WeaponSystemClientside.ammoCapacity = nil
	WeaponSystemClientside.currentAmmo = nil
	WeaponSystemClientside.magCapacity = nil
	WeaponSystemClientside.currentMag = nil
	WeaponSystemClientside.shotCooldown = nil
end

-- This lets the server know whether the weapon is equipped or not
tool.Equipped:Connect(function()
	WeaponSystemClientside.equipped = true
	handOffVariables()
end)

tool.Unequipped:Connect(function()
	WeaponSystemClientside.equipped = false
	deleteVariables()
end)

This is the ModuleScript which should recieve the value. The :Update function is being renderstepped from a different module - this definitely works as intended, I’ve tested.

local ReplicatedFirst = game:GetService("ReplicatedFirst")
local WeaponSystemLink = ReplicatedFirst:WaitForChild("WeaponSystemLink")

local WeaponSystemClientside = {}
WeaponSystemClientside.__index = WeaponSystemClientside

function WeaponSystemClientside.new()
	local self = setmetatable({}, WeaponSystemClientside)
	
	self.BaseWeapon = require(script:WaitForChild("BaseWeapon"))
	self.Animation = require(script:WaitForChild("Animation"))
	self.Effects = require(script:WaitForChild("Effects"))
	
	self.passAction = WeaponSystemLink:WaitForChild("PassActions")
	
	-- These are gained from ControlModule
	self.isPrimaryFire = false
	self.isSecondaryFire = false
	self.isLastWeaponSwitch = false

	-- These are gained from the weapon's configuration
	self.ammoCapacity = nil
	self.currentAmmo = nil
	self.magCapacity = nil
	self.currentMag = nil
	self.shotCooldown = nil

	-- This is gained from the weapon when equipped
	self.equipped = false :: boolean
	
	-- This is sent to the server so it knows when the gun can fire
	self.enabled = false
	return self
end

function WeaponSystemClientside:Update()
	print(self.equipped)
end

return WeaponSystemClientside

A lot of the information in these scripts is probably not relevant but I wanted to make 100% sure anything that is important is included.

Any help would be appreciated, thanks.

Did you mean to say this instead?

local WeaponSystemClientside = require(ControlModule.WeaponSystemClientside).new()

That still just returns nil.

Also it should just change the values in the existing metatable rather than make a new one

What you said doesn’t make sense, do you understand what a metatable is?

You can’t access .equipped without calling new, the module just returns a list of functions along with an .__index value, which is just a reference to itself. You have to call .new() to be able access an object with the .equipped property.

I fear you are doing it incorrectly.

Figured out a solution without dealing with the individual tool at all.

Basically this system is built off the back of Roblox’s control/movement scripts (if you’ve ever dived into them) so everything is run off of that - I can’t just create a new metatable for every single tool picked up otherwise it’d break. I’m doing this cause I eventually want to implement the ability to set your own custom keybinds but that’s an entire other challenge for later on.

As for the solution, I just check if the player has a tool equipped in :Update (which is renderstepped), if it does I set self.equipped to true, if not then self.equipped is false. Seems to work well enough.

Thanks anyways!

So, you’re telling me that:

print(require(ControlModule.WeaponSystemClientside).new().equipped)

This prints nil?

My mistake, that prints false (oops), however doing ControlModule.WeaponSystemClientside.new() would break existing scripts. This sytem functions the same way as Roblox movement does where the module 's :Update function is renderstepped when the player joins. I think this is the same or a similar way that Roblox’s weapons work although I only looked at that briefly so I could be wrong.

Seems weird but I’m doing it this way to integrate into my custom movement system, which is integrated into Roblox’s movement system, and so that I can eventually have a custom keybind system allowing the player to change their keybinds which requires everything to be run to an extent under the same umbrella. Also it’s nicer to copy across to new games.

1 Like