Problem with OOP inheritance

a fairytale with no happy ending

once upon a time, this module worked. i’d call for a linear hitbox using

local hitbox = hbox.Linear.new{
	Origin = rootPart.Position + rootPart.CFrame.lookVector ,
	Direction = rootPart.CFrame.lookVector * 10,
	Radius = 2,
	Visible = true,
	IgnoreList = {self.Character}
}

then i’d activate it simply using

hitbox:Activate()

then i looked at my module and thought to myself
“hmm… i wonder how nice this would look if i applied the DRY principle…”

so i figured i’d make something like a superclass called _hitbox and three subclasses called _normalHitbox, _linearHitbox, and _complexHitbox that will inherit properties from _hitbox like Radius, Visible, etc. so that i Don’t Repeat [My]self.

unfortunately, that destroyed my entire module and now self.Radius returns nil whenever the linear activation function calls for it.

the end

tl;dr: properties aren’t being returned correctly and i have no idea why. i desperately need help as i’ve been working on this for like 2 days and i managed to destroy it in a matter of minutes

local library = require(game.ReplicatedStorage.Library)
local create = library.Create

--hitbox superclass

local _hitbox = {}
_hitbox.__index = _hitbox

_hitbox.new = function(parameters)
	local newHitbox = {}
	
	setmetatable(newHitbox, _hitbox)
	
	newHitbox.Radius = 5
	newHitbox.Visible = true
	newHitbox.IgnoreList = {}
	newHitbox.Function = function(humanoid)
		print(humanoid:GetFullName())
	end
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	print'hitbox generated'
	
	return newHitbox
end

--normal hitbox

local _normalHitbox = {}
_normalHitbox.__index = _hitbox
setmetatable(_normalHitbox, _hitbox)

_normalHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _normalHitbox)
	
	newHitbox.Position = Vector3.new(0, 0, 0)
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_normalHitbox.Activate = function(self)
	self.Hit = {}
	if self.Visible then
		local part = create'Part'{
			Parent = workspace,
			Name = 'Visual',
			Position = self.Position,
			Transparency = 1,
			CanCollide = false,
			Anchored = true
		}
		table.insert(self.IgnoreList, part)
		create'SphereHandleAdornment'{
			Parent = part,
			Color3 = Color3.new(1, 0, 0),
			Transparency = 0.8,
			AlwaysOnTop = true,
			Adornee = part,
			Radius = self.Radius,
			ZIndex = 0
		}
		print'visualizer made'
	end
	create'Explosion'{
		Name = 'Hitbox',
		Position = self.Position,
		BlastRadius = self.Radius,
		BlastPressure = 0,
		DestroyJointRadiusPercent = 0,
		ExplosionType = Enum.ExplosionType.NoCraters,
		Visible = false,
		Parent = workspace,
		Hit = function(part, distance)
			local hitIgnore = true
			for index, ignore in next, self.IgnoreList do
				if part == ignore or part:IsDescendantOf(ignore) then
					hitIgnore = false
				end
			end
			if hitIgnore then
				local humanoid = part.Parent:FindFirstChildOfClass'Humanoid'
				if humanoid and not table.find(self.Hit, part.Parent) then
					table.insert(self.Hit, part.Parent)
					if self.Visible then
						print('got', part.Parent:GetFullName())
					end
					self.Function(humanoid)
				end
			end
		end
	}
end

--linear hitbox

local _linearHitbox = {}
_linearHitbox.__index = _linearHitbox
setmetatable(_linearHitbox, _hitbox)

_linearHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _linearHitbox)
	
	newHitbox.Origin = Vector3.new(0, 0, 0)
	newHitbox.Direction = Vector3.new(0, 0, 0)
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_linearHitbox.Activate = function(self)
	local increments = (self.Origin - (self.Origin + self.Direction)).magnitude / self.Radius
	print(increments)
	for increment = 0, increments, 1 do
		local hitbox = _normalHitbox.new{
			Position = self.Origin + self.Direction * (increment / increments),
			Radius = self.Radius,
			Visible = self.Visible,
			IgnoreList = self.IgnoreList,
			Function = self.Function
		}
		hitbox:Activate()
	end
end

--complex hitbox

local _complexHitbox = {}
_complexHitbox.__index = _complexHitbox
setmetatable(_complexHitbox, _hitbox)

_complexHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _complexHitbox)
	
	newHitbox.Points = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 5), Vector3.new(5, 0, 5)}
	newHitbox.Closed = false
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_complexHitbox.Activate = function(self, parameters)
	for point = 1, self.Closed and #self.Points or #self.Points - 1 do
		local hitbox = _linearHitbox.new({
			Origin = self.Points[point],
			Direction = CFrame.new(self.Points[point]):PointToObjectSpace(self.Points[point == #self.Points and self.Closed and 1 or point + 1]),
			Radius = self.Radius,
			Visible = self.Visible,
			IgnoreList = self.IgnoreList,
			Function = self.Function
		})
		hitbox:Activate()
	end
end

return {
	Normal = _normalHitbox, 
	Linear = _linearHitbox,
	Complex = _complexHitbox
}

whenever i call for a linear hitbox, it returns this

02:34:45.900 - ReplicatedStorage.Library.Hitbox:131: attempt to perform arithmetic (div) on number and nil
02:34:45.901 - Stack Begin
02:34:45.902 - Script 'ReplicatedStorage.Library.Hitbox', Line 131

thanks in advance

edit: accidentally posted the semi-old one rip
edit2: i posted the wrong one again bruh

Hi. I’m not an expert, but I think there are two errors. Here’s the code I corrected.

local _hitbox = {}
_hitbox.__index = _hitbox

_hitbox.new = function(parameters)
	local newHitbox = {}
	
	setmetatable(newHitbox, _hitbox)
	
	newHitbox.Radius = 5
	newHitbox.Visible = true
	newHitbox.IgnoreList = {}
	newHitbox.Function = function(humanoid)
		print(humanoid:GetFullName())
	end
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	print'hitbox generated'
	
	return newHitbox
end

--normal hitbox

local _normalHitbox = {}
_normalHitbox.__index = _normalHitbox
setmetatable(_normalHitbox, _hitbox)

_normalHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _normalHitbox)
	
	newHitbox.Position = Vector3.new(0, 0, 0)
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_normalHitbox.Activate = function(self)
	self.Hit = {}
	if self.Visible then
		
		local part = Instance.new('Part')
		part.Parent = workspace
		part.Name = 'Visual'
		part.Position = self.Position
		part.Transparency = 1
		part.CanCollide = false
		part.Anchored = true
		
		table.insert(self.IgnoreList, part)
		
		local sha = Instance.new('SphereHandleAdornment')
		sha.Parent = part
		sha.Color3 = Color3.new(1, 0, 0)
		sha.Transparency = 0.8
		sha.AlwaysOnTop = true
		sha.Adornee = part
		sha.Radius = self.Radius
		sha.ZIndex = 0
		
		print'visualizer made'
	end
	
	local exp = Instance.new'Explosion'
	exp.Name = 'Hitbox'
	exp.Position = self.Position
	exp.BlastRadius = self.Radius
	exp.BlastPressure = 0
	exp.DestroyJointRadiusPercent = 0
	exp.ExplosionType = Enum.ExplosionType.NoCraters
	exp.Visible = false
	exp.Parent = workspace
	
	local onHit = function(part, distance)
		local hitIgnore = true
		
		for index, ignore in next, self.IgnoreList do
			if part == ignore or part:IsDescendantOf(ignore) then
				hitIgnore = false
			end
		end
		
		if hitIgnore then
			local humanoid = part.Parent:FindFirstChildOfClass'Humanoid'
			if humanoid and not table.find(self.Hit, part.Parent) then
				table.insert(self.Hit, part.Parent)
				if self.Visible then
					print('got', part.Parent:GetFullName())
				end
				self.Function(humanoid)
			end
		end
	end
	exp.Hit:Connect(onHit)
end

--linear hitbox

local _linearHitbox = {}
_linearHitbox.__index = _linearHitbox
setmetatable(_linearHitbox, _hitbox)

_linearHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _linearHitbox)
	
	newHitbox.Origin = Vector3.new(0, 0, 0)
	newHitbox.Direction = Vector3.new(0, 0, 0)
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_linearHitbox.Activate = function(self)
	local increments = (self.Origin - (self.Origin + self.Direction)).magnitude / self.Radius
	print(increments)
	for increment = 0, increments, 1 do
		local hitbox = _normalHitbox.new{
			Position = self.Origin + self.Direction * (increment / increments),
			Radius = self.Radius,
			Visible = self.Visible,
			IgnoreList = self.IgnoreList,
			Function = self.Function
		}
		hitbox:Activate()
	end
end

--complex hitbox

local _complexHitbox = {}
_complexHitbox.__index = _complexHitbox
setmetatable(_complexHitbox, _hitbox)

_complexHitbox.new = function(parameters)
	local newHitbox = _hitbox.new(parameters)
	
	setmetatable(newHitbox, _complexHitbox)
	
	newHitbox.Points = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 5), Vector3.new(5, 0, 5)}
	newHitbox.Closed = false
	
	for index, parameter in next, parameters do
		if newHitbox[index] then
			newHitbox[index] = parameter
		end
	end
	
	return newHitbox
end

_complexHitbox.Activate = function(self, parameters)
	for point = 1, self.Closed and #self.Points or #self.Points - 1 do
		local hitbox = _linearHitbox.new({
			Origin = self.Points[point],
			Direction = CFrame.new(self.Points[point]):PointToObjectSpace(self.Points[point == #self.Points and self.Closed and 1 or point + 1]),
			Radius = self.Radius,
			Visible = self.Visible,
			IgnoreList = self.IgnoreList,
			Function = self.Function
		})
		hitbox:Activate()
	end
end

return {
	Normal = _normalHitbox, 
	Linear = _linearHitbox,
	Complex = _complexHitbox
}

I don’t know exactly what it does, but it doesn’t give errors.
PD: I didn’t use Create because I don’t have it handy.

1 Like

You have to use : for it to work, and you don’t really need self in the parameters because it autofills:

_normalHitbox:Activate = function()

This applies to all other functions.

wow thanks! it works flawlessly. can’t tell what changes you made to be honest but it works lol

--normal hitbox
local _normalHitbox = {}
_normalHitbox.__index = _hitbox  -- here by: _normalHitbox.__index = _normalHitbox

and

Hit = function(part, distance) -- I remember that events are handled differently with Create

I stopped using Create because I always forgot how it works.

That wouldn’t work, it’d throw an error that would look something like this: Expected '(', '{' or <string> when parsing function call, got '='

Besides, it’s perfectly fine to do what they did.