Just to make this clear, I’m relatively new to the concept of OOP, therefore there are bound to be small mistakes or misuse of parameters.
In Short
My method is returning a nil value even though I have declared the value in the constructor.
What I’ve Tried
I’ve previously assigned a new credit int from a server script to the table, which showed up for the method, but wouldn’t work if I just assigned the variable data from inside the module.
local creditSystem = {}
creditSystem.__index = creditSystem
function creditSystem.new(userID)
local self = {
credits = 1000
}
setmetatable(self, creditSystem)
return self
end
function creditSystem:DeductCredits(amount)
self.credits += amount -- self.credits = nil, even though it's declared to have 1000 [This is line 14]
local creditUpdateRemote = game.ReplicatedStorage.Events.CreditUpdate
creditUpdateRemote:Fire(self.credits)
end
return creditSystem
Amount Script
local modulePath = game.ServerScriptService.Server_Script_Essentials
local creditSystem = require(modulePath.Shop_Credits_Controller.Credits_Module)
script.Parent.ClickDetector.MouseClick:Connect(function(player)
creditSystem:DeductCredits(-200)
end)
Any advice/tips are appreciated + any resources that might be useful for further use.
local creditSystem = {}
creditSystem.__index = creditSystem
function creditSystem.new(userId)
local NewCreditSystem = {}
setmetatable(NewCreditSystem , creditSystem)
NewCreditSystem.credits = 1000
return NewCreditSystem
end
function creditSystem:DeductCredits(amount)
self.credits += amount
local creditUpdateRemote = game.ReplicatedStorage.Events.CreditUpdate
creditUpdateRemote:Fire(self.credits)
end
This is because when you’re passing self to DeductCredits, you’re actually passing the module as self, not the CreditSystem object.
So, what I’d do is save the CreditSystem object to a table inside the CreditSystem module, or use a BindableFunction to return the active credit object for that userId, but I’ll use the first proposed solution for simplicity:
local creditSystem = {}
creditSystem.__index = creditSystem
creditSystem._activeCreditSystemObjects = {}
function creditSystem.new(userID)
if creditSystem._activeCreditSystemObjects[userId] then
return creditSystem._activeCreditSystemObjects[userId] -- find the existing credit object for this UserId, if it exists then return it
end
-- it doesn't exist so create a new one
local self = {
credits = 1000
}
setmetatable(self, creditSystem)
creditSystem._activeCreditSystemObjects[userId] = self -- store the new credit object to be referenced if needed
return self
end
function creditSystem:DeductCredits(amount)
self.credits += amount
local creditUpdateRemote = game.ReplicatedStorage.Events.CreditUpdate
creditUpdateRemote:Fire(self.credits)
end
return creditSystem
Now, to deduct the credits, you can use the creditSystem.new function to return the existing credit object, or a new one if one doesn’t exist yet:
script.Parent.ClickDetector.MouseClick:Connect(function(player)
local creditObject = creditSystem.new(player.UserId)
creditObject:DeductCredits(-200)
end)
Basically, when you call require(moduleScript), it returns the module which contains the constructor (module.new()). When you call module.new(), only then does it return an object, you have to save this object somewhere in memory (eg. as a variable, in a table etc) in order to use its methods and manipulate it.
local module = require(path.to.module)
local var = module:method()
-- ^ is the same as doing
local var = module.method(module)
-- which is how `self` gets defined when you define a function like
local tbl = {}
function tbl:method()
-- here, the variable "self" is defined as tbl automatically, even though we never manually assigned a value to it
end
-- furthermore, the function declaration above is essentially the same as doing
function tbl.method(self)
end
So, how is this relevant?
When you’re calling object:method(), you’re passing the object as self, this object’s metatable has the __index metamethod which sort of gives the illusion that object.method exists despite being inside of the module, not the object.
local class = {}
class.__index = class
function class.new()
local object = {}
object.property = 1
setmetatable(object, class) -- allows any function inside of the class module to be called to manipulate the `object` table
return object
end
-- now, let's create a function to manipulate an object
function class.method(self) -- remember, same as doing `function class:method()`
print(self.property)
end
Now, in another script,
local class = require(path.to.module)
class:method() -- this will print nil because we're passing the "class" table as self, which does not contain the "property" index
local newObject = class.new()
newObject:method() -- this will print 1 because we're passing the "newObject" object as self, which does contain the "property" index"
class.method(newObject) -- this will also print 1 because we're passing the "newObject" object as self
newObject.method(class) -- this will print nil because we're passing the "class" table as self, which once again does not contain the "property" index.