I think I’m doing this wrong, following the example of OP for creating and inheriting metatables and their methods/functions. Below in the BaseTool section I create what I call a template for my custom Tool object that all other “tools” (such as a sword or a gun) use for a template. I have an odd thing going on, whenever I call Tool:Destroy() on a BaseTool (Tool) object it’ll destroy it, and when I call RangedWeapon:Destroy() it’ll also be destroyed. But when I try to call :Destroy() on the PropaneTank, whether it be done by calling PropaneTank:Destroy() (when the object is spawned by using PropaneTank.new()) or by calling self:Destroy(), the script will error and say that Destroy is a nil value.
I followed step 2.2 of the original post, except the only change I made was by adding __index inside of the PropaneTank.new() function so I could read the properties of the PropaneTank… But if I remove that then my script will also break… Yeah I’m not sure what’s going on anymore. Here’s my code to see what I’m doing.
---------------------
-- ROBLOX Services --
---------------------
local RunService = game:GetService("RunService")
local RS = game:GetService("ReplicatedStorage")
-------------------
-- ModuleScripts --
-------------------
local Utilities = require(RS:WaitForChild("Utilities"))
---------------
-- Variables --
---------------
local Weapons = {
Ranged = {},
Melee = {},
Other = {},
}
for Index, ModuleScript in pairs(RS:WaitForChild("Weapons"):WaitForChild("Ranged"):GetChildren()) do
if ModuleScript:IsA("ModuleScript") then
local DataToCopy = require(ModuleScript)
local DataToSave = Utilities:DeepCopy(DataToCopy)
DataToSave.Type = "Weapon"
Weapons.Ranged[#Weapons.Ranged+1] = DataToSave
end
end
for Index, ModuleScript in pairs(RS:WaitForChild("Weapons"):WaitForChild("Melee"):GetChildren()) do
if ModuleScript:IsA("ModuleScript") then
local DataToCopy = require(ModuleScript)
local DataToSave = Utilities:DeepCopy(DataToCopy)
DataToSave.Type = "Weapon"
Weapons.Ranged[#Weapons.Ranged+1] = DataToSave
end
end
for Index, ModuleScript in pairs(RS:WaitForChild("Weapons"):WaitForChild("Support"):GetChildren()) do
if ModuleScript:IsA("ModuleScript") then
local DataToCopy = require(ModuleScript)
local DataToSave = Utilities:DeepCopy(DataToCopy)
DataToSave.Type = "Support"
Weapons.Other[#Weapons.Other+1] = DataToSave
end
end
for Index, ModuleScript in pairs(RS:WaitForChild("Weapons"):WaitForChild("Other"):GetChildren()) do
if ModuleScript:IsA("ModuleScript") then
local DataToCopy = require(ModuleScript)
local DataToSave = Utilities:DeepCopy(DataToCopy)
DataToSave.Type = "Other"
Weapons.Other[#Weapons.Other+1] = DataToSave
end
end
local function FindTool(ToolName)
for FolderName, FolderContents in pairs(Weapons) do
for Index, Tool in pairs(FolderContents) do
-- print(Tool.Name)
if Tool.Name == ToolName then
return Tool, FolderName
end
end
end
end
local function CreatToolAnimationData(Parent, IndexName, OriginalTable, ToolName)
local Type = type(OriginalTable)
local copy
if Type == "table" then
copy = {}
for orig_key, orig_value in next, OriginalTable, nil do
copy[CreatToolAnimationData(Parent, orig_key, orig_key, ToolName)] = CreatToolAnimationData(Parent, orig_key, orig_value, ToolName)
end
elseif Type == "number" then
local AnimationObject = Instance.new("Animation")
AnimationObject.Name = ToolName.."_"..IndexName
AnimationObject.AnimationId = "http://www.roblox.com/asset/?id="..OriginalTable
AnimationObject.Parent = Parent
copy = {AnimationObject, nil}
else
copy = OriginalTable
end
return copy
end
local function FindHighestFiringMode(ToolData)
local FiringModes = ToolData.FiringModes
if #FiringModes > 1 then
local SelectedMode = 1
for Index, Mode in pairs(FiringModes) do
for _,M in pairs({"Semi", "Burst", "Auto"}) do
if Mode == M then
SelectedMode = Index
end
end
end
return SelectedMode
else
return 1
end
end
local Service = {}
Service.RetrieveToolData = FindTool
-------------------------
-- Base Tool Class API --
-------------------------
Service.Tool = nil
do
local Class = {}
Class.__index = Class
function Class.new(ToolName, Player)
local Data = FindTool(ToolName)
if Data ~= nil then
local Tool = {}
setmetatable(Tool, Class)
Tool = Utilities:DeepCopy(Data)
Tool.Parent = Player
Tool.ToolModel = Tool.ToolModel:Clone()
Tool.ToolModel.PrimaryPart = Tool.ToolModel.Handle
Utilities:WeldTool(Tool.ToolModel)
Tool.SavedWeldData = Utilities:SaveWeldData(Tool.ToolModel)
Tool.ToolModel.Parent = Tool.Parent
Tool.Animations = CreatToolAnimationData(Tool.ToolModel, Tool.Name, Tool.Animations, Tool.Name)
-- Tool.Equipped
local Equipped = Instance.new("BindableEvent", Tool.ToolModel)
Class.Equipped = Equipped.Event
-- Tool.Unequipped
local Unequipped = Instance.new("BindableEvent", Tool.ToolModel)
Class.Unequipped = Unequipped.Event
return Tool
else
spawn(function() error("Error there is no such tool named "..tostring(ToolName)..".") end)
return nil
end
end
function Class:Destroy()
local n = self.Name
self.ToolModel:Destroy()
self = nil
print(n, "was successfully destroyed!")
end
Service.Tool = Class
end
-----------------------------
-- Ranged Weapon Class API --
-----------------------------
Service.RangedWeapon = nil
do
local Class = {}
Class.__index = Class
setmetatable(Class, Service.Tool)
function Class.new(WeaponName, Player, AmountOfAmmo)
local RangedWeapon = Service.Tool.new(WeaponName, Player)
if RangedWeapon == nil then spawn(function() error("Error, BaseWeapon is nil.") end) return end
local MaximumAmountAllowed = (RangedWeapon.MaxAmountOfMagazines * RangedWeapon.MagazineCapacity)
if AmountOfAmmo >= MaximumAmountAllowed then
AmountOfAmmo = MaximumAmountAllowed
end
setmetatable(RangedWeapon, Class)
RangedWeapon.SelectedFiringMode = FindHighestFiringMode(RangedWeapon)
RangedWeapon.TotalAmmo = AmountOfAmmo
RangedWeapon.MagAmmo = RangedWeapon.MagazineCapacity
return RangedWeapon, AmountOfAmmo
end
Service.RangedWeapon = Class
end
----------------------------
-- Melee Weapon Class API --
----------------------------
Service.PropaneTank = nil -- this is the only proper metatable...
do
local Class = {}
Class.__index = Class
setmetatable(Class, Service.Tool)
function Class.new(Parent)
local PropaneTank = Service.Tool.new("Propane Tank")
local DamagableTag = Instance.new("BoolValue")
DamagableTag.Name = "Damagable"
DamagableTag.Value = true
DamagableTag.Parent = PropaneTank.ToolModel
PropaneTank.BackingTable = {IsDead = false}
PropaneTank.CreationTimeStamp = tick()
PropaneTank.Name = nil ; PropaneTank.BackingTable.Name = "Propane_Tank" ; PropaneTank.ToolModel.Name = PropaneTank.BackingTable.Name
PropaneTank.Health = nil; PropaneTank.BackingTable.Health = 5
PropaneTank.Position = nil
PropaneTank.CFrame = nil
PropaneTank.Parent = nil ; PropaneTank.ToolModel.Parent = Parent
setmetatable(PropaneTank, Class)
Class.__index = function(self, Index)
return self.BackingTable[Index]
end
Class.__newindex = function(self, Index, Value)
--rawset(self, Key, Value)
self.BackingTable[Index] = Value
-- rawset(PropaneTank.BackingTable, Key, Value)
if Index == "Name" then
self.ToolModel.Name = Value
elseif Index == "Parent" then
self.ToolModel.Parent = Value
elseif Index == "CFrame" then
self.ToolModel:SetPrimaryPartCFrame(Value)
self.BackingTable.Position = Value.p
elseif Index == "Position" then
--[[
local OldCF = self.BackingTable.CFrame
local angles = OldCF - OldCF.p
self.BackingTable.Position = Value.p
--]]
self.BackingTable.Position = Value.p
self.ToolModel.PrimaryPart.Position = Value
elseif Index == "Health" then
if self.BackingTable.Health == 0 and not self.BackingTable.IsDead then
self.BackingTable.IsDead = true
local e = Instance.new("Explosion")
e.Position = self.ToolModel.PrimaryPart.CFrame.p
e.DestroyJointRadiusPercent = 0
e.BlastRadius = 15
e.Parent = game.Workspace
for Index, Zombie in pairs(_G.Zombies) do
local Distance = (e.Position - Zombie.Torso.Position).Magnitude
if Distance <= e.BlastRadius then
Zombie:TakeDamage(1000)
end
end
if _G.MapData then
for Index, Tank in pairs(_G.MapData.PropaneTanks) do
if Tank ~= self and not Tank.BackingTable.IsDead and Tank.ToolModel.PrimaryPart ~= nil then
local Distance = (e.Position - Tank.ToolModel.PrimaryPart.CFrame.p).Magnitude
if Distance <= e.BlastRadius then
Tank.Health = 0
end
end
end
end
-- only for testing
self:Destroy() -- delay(0.5, function() self.BackingTable.IsDead = false end)
end
end
-- warn(self.Name.."."..(Index).." property was changed to "..tostring(Value)..".")
end
return PropaneTank
end
function Class:Destroy()
self.ToolModel:Destroy()
end
Service.PropaneTank = Class
end
return Service