I’ve run into this weird issue where I can’t use :WaitForChild on a wrapper for some reason. No idea why but it’s kind of frustrating.
['CreateDummy'] = function(self)
local currentDummy = dummy:Clone()
local humanoidRootPart = currentDummy:WaitForChild('HumanoidRootPart')
local humanoid = currentDummy:WaitForChild('Humanoid')
currentDummy.Parent = dummyFolder
local pathContainer = currentDummy:WaitForChild('Path')
pathContainer.Value = pathfindingService:CreatePath()
local funcs = { -- this is just the added functionality, nothing here should be causing the issue as it works smoothly aside from the error
['MoveToActive'] = false;
['Status'] = 'Roaming';
['GenerateRandomPoint'] = function(self, radius)
local currentPos = humanoidRootPart.Position
local atts = 0
repeat
local randomPos = currentPos + Vector3.new(math.random(-radius, radius), 0, math.random(-radius, radius))
atts += 1
print(atts)
pathContainer.Value:ComputeAsync(currentPos, randomPos)
until pathContainer.Value.Status == Enum.PathStatus.Success
end;
['PlayPath'] = function(self)
self.MoveToActive = true
for i,v in ipairs(pathContainer.Value:GetWaypoints()) do
local currentWaypoint = Instance.new('Part')
currentWaypoint.Parent = debugFolder
currentWaypoint.Material = Enum.Material.Neon
currentWaypoint.Size = Vector3.new(1,1,1)
currentWaypoint.CanCollide = false
currentWaypoint.Shape = Enum.PartType.Ball
currentWaypoint.Anchored = true
currentWaypoint.Position = v.Position
currentWaypoint.CanCollide = false
self.Dots[#self.Dots + 1] = currentWaypoint
end
for i,v in ipairs(pathContainer.Value:GetWaypoints()) do
if self.MoveToActive then
humanoid:MoveTo(v.Position)
print('mt', v.Position)
humanoid.MoveToFinished:Wait()
print('mtf')
else
break
end
end
pathContainer.Value = pathfindingService:CreatePath()
end;
['CancelPath'] = function(self)
self.MoveToActive = false
end;
['DestroyDots'] = function(self)
for i,v in pairs(self.Dots) do
v:Destroy()
end
end;
['Dots'] = {}
}
local meta = { -- this is the metatable
__index = dummy
}
return setmetatable(funcs, meta)
end;
Now, from the other script that actually requires the dummy,
local currentDummy = dummyHandler:CreateDummy()
local dummyHumanoidRootPart = currentDummy:WaitForChild('HumanoidRootPart') -- this is the line that errors
I’m returning the metatable whose __index is dummy, that metatable is assigned to the currentDummy value. It recognizes that WaitForChild is a function (otherwise it would error with attempt to call a nil value) so that’s what is causing the issue. I just don’t know how to fix it.
It’s not going to error, :WaitForChild is essentially the same as indexing it just with an added safety measure if the child didn’t replicate instantly for whatever reason. currentDummy is also already being assigned as the metatable. It also doesn’t matter if I haven’t predefined metatable.WaitForChild, it should inherit all members from the actual dummy model instance.
Also, as I told you before, if metatable.WaitForChild didn’t exist, it would give the “attempt to index nil” error.
If you do type(metatable.WaitForChild) it will return ‘function’ because it does exist, the self variable just isn’t being passed for whatever reason.
@JackscarIitt Yeah, I took a look into that post but it isn’t what I was looking for unfortunately. The self variable just isn’t being passed and I can’t figure out why. Basically on the backend it’s probably something like function module.WaitForChild(self, name) if not self then error('expected : not . (insert rest of error message lol)'). Using a : just automatically passes the self variable which is why game:GetService(‘Workspace’) is the same as game.GetService(game, ‘Workspace’)
The problem is, you’re passing currentDummy as the first argument to the WaitForChild method, this is essentially doing dummy.WaitForChild(currentDummy, "HumanoidRootPart"), not dummy.WaitForChild(dummy, "HumanoidRootPart"), because x:y(...) is the sugar syntax for x.y(x, ...) (I guess you already knew this but the x in that example doesn’t refer to the __index metafield). __index metafield doesn’t really mean it’ll inherit the value from the field, it means it’ll index whatever the field is set for __index as a fallback for indexing.
Do you have any suggestions for how I could go about solving it though? I was thinking like
__index = function(tbl, key)
if currentDummy[key] then
then if I did
tbl:WaitForChild('str')
it would return that it exists but I don’t know how I would get the argument passed.
Another problem is that there’s no protection if it errors which is kind of where I’m stuck. It would probably be easily fixable if I could get the args passed but I’m not really sure. I could probably use pcall but I don’t really want to use that method since it’s kind of clunky. It also runs me back into the problem of not knowing how to get the arguments passed.
I’m not sure exactly which argument you mean when you say “get the argument passed”?
Are you referring to the original dummy table being used as the index?
The colon operator is syntactic sugar to call a function in a table. Doing object:func(...) is the same thing as doing object.func(object, ...).
When you call the function WaitForChild with a colon, it’s essentially doing currentDummy.WaitForChild(currentDummy, 'HumanoidRootPart'). currentDummy is not an instance so that is why the error occurs.
Try setting this as your __index metamethod:
__index = function(_, index, value)
local member = dummy[index]
if typeof(member) == "function" then
return function(_, ...)
return member(dummy, ...)
end
end
return member
end