I have a really simple issue that's been keeping me riled the past hour pertaining to looping through two tables and matching their keys up.
On one table, it's getting the children in a folder (They are 6 IntValues but I really want their Name property), and on the other table, its a dictionary of 6 keys with their values (Those values are intergers). What I need it to do is match up the IntValues' names from the first table with the keys on the second table. The two tables are seperated into a Script and ModuleScript but that shouldn't matter.
--This is a function that fires from a RemoteEvent local function onSentChosenClass(plr, chosenClass) if chosenClass then for i, v in pairs(PlayerStatsFolder:FindFirstChild("Attributes"):GetChildren()) do --Trying to debug the Metatable thats in the ModuleScript. Prints nil. How dandy. print(ModuleScript.AttributeMetatable(chosenClass)[v.Name]) --[[ Previous attempt before the use of _index thats commented out but prints the values of the 2nd table. Ignore the if statement, its always false. if v.Name == ModuleScript.Classes[chosenClass].classAtt[v.Name] then print(ModuleScript.Classes[chosenClass].classAtt[v.Name]) end ]] end end end
local settings = {} --Literally copied/pasted from Wiki local returnMetatable = { _index = function(self, index) return index end } --This returns the table but I don't seem to be getting the right key/attribute function settings.AttributeMetatable(className) setmetatable(settings.Classes[className].classAtt, returnMetatable) return returnMetatable end settings.Classes = { Warrior = { classAtt = { -- I want the names of the keys (STR, CON, etc) STR = 7, CON = 5, DEF = 6, DEX = 4, INT = 4, LUC = 4 } } } return settings
What I have written down and also have reworked alot is using a metatable and _index to return the key. I got this off Roblox Wiki's metamethods but these are so hard to understand for a below average scripter like my self.
After matching the second table's keys with the names of the IntValues' Name property then its simply setting the IntValues' value to the 2nd table's values. Freaking boring and confusing explanation, right?
I don't expect a rework made to my code, yet that with an explanation is all the more plentiful. Thanks.
Metatables let you do fancy things and there is almost always an alternative solution. Briefly summarized, the __index
metamethod lets you change how a value is accessed and __newindex
lets you change how a value is assigned. (Note that in your code, you missed a 2nd underscore -- it's __index
, not _index
.) Metatables are one of the suggested methods for creating classes (and when you follow the pattern correctly it works fine), but there are alternatives.
In your case, you could make do with simple tables:
--Module Script local settings = {} local classes = { Warrior = { -- Note: no need for 'classAtt' STR = 7, CON = 5, --etc }, --Other classes here } function settings.GetClass(class) return classes[class] end return settings
Technically you could just return classes
instead of settings
; then, instead of ModuleScript.GetClass("Warrior")
you'd just do ModuleScript["Warrior"]
. However, the above method lets you easily add more functionality later.
Your other script:
local Settings = require(ModuleScriptPathHere) -- might as well give it a reasonable name local function onSentChosenClass(plr, chosenClass) if not chosenClass then error("No chosen class") end -- I expect this shouldn't even happen if you don't have a bug; when you're finished debugging, consider kicking the player instead -- they're probably an exploiter (or they discovered a bug you didn't). It's OK to error in a RemoteEvent; the event will still trigger the next time the event is activated. local ch = PlayerStatsFolder:FindFirstChild("Attributes"):GetChildren() local value local class = Settings.GetClass(chosenClass) if not class then error("Invalid class: " .. tostring(chosenClass)) end -- like before, an exploiter might trigger this, or you have a bug. Remember that they can theoretically pass in any value they can dream up. for i = 1, #ch do -- you can also use ipairs. pairs also works, but is the least efficient option of all of them, in this case. Do whatever you're most comfortable with. value = ch[i] value.Value = class[value.Name] or error("Class " .. tostring(chosenClass) .. " does not contain value for " .. tostring(value.Name)) -- this error is solely to make sure you didn't mis-configure anything end end
Since you're already using ModuleScripts, you should consider forgetting about the IntValues and just using the 'settings' ModuleScript of yours. If you intend to increase the value of these IntValues as players level up, you would need another ModuleScript to maintain the current value of everyone (replacing what the IntValues do), but that would arguably be an improvement. (If you're struggling with ModuleScripts, it's fine to keep them as IntValues, especially if the rest of your scripts already depend on them.)
Due to how you have the for
loop set up (to iterate over the children), you can safely add other entries into the Warrior
table that have nothing to do with the 6 IntValues. You can even add functions, which makes it start looking like a class. If you do want class functionality, you might want to study different ways of making them:
http://lua-users.org/wiki/ObjectOrientedProgramming
The style I use is this:
function MyClass(value) local self = {} local valueIsEven = value % 2 == 0 function self:GetValue() -- technically you could use "." instead of ":" since you never need 'self' passed in to the function, but to mimic Roblox notation I use ":" return value end function self:Increment() value = value + 1 valueIsEven = value % 2 == 0 -- actually it would probably be more efficient to calculate this on request rather than here, but this is just an example end function self:ValueIsEven() return valueIsEven end return self end local obj = MyClass(5) obj:Increment() print(obj:GetValue(), obj:ValueIsEven()) -- 6, true --Let's say you wanted a class that inherits from MyClass and also saves the previous value: function MyClass2(value) local self = MyClass(value) local baseIncrement = self.Increment local prevValue = value function self:Increment() prevValue = value baseIncrement(self) end function self:GetPrevValue() return prevValue end return self end local obj = MyClass2(10) print(obj:GetPrevValue(), obj:GetValue(), obj:ValueIsEven()) -- 10, 10, true obj:Increment() print(obj:GetPrevValue(), obj:GetValue(), obj:ValueIsEven()) -- 10, 11, false obj:Increment() print(obj:GetPrevValue(), obj:GetValue(), obj:ValueIsEven()) -- 11, 12, true