This is my first time using OOP, so I need some feedback to see if there’s anything that needs to be changed. This is a server and client script, which handles both the server and client, for better replication.
local ToolClass = {} -- Programmer : Galicate --// SERVICES //-- local PLAYER_SERVICE = game:GetService("Players") local REPLICATED_STORAGE = game:GetService("ReplicatedStorage") local RUN_SERVICE = game:GetService("RunService") local DEBRIS = game:GetService("Debris") --// METATABLES //-- ToolClass.__index = ToolClass --// CONSTANTS //-- local RemoteInstance = REPLICATED_STORAGE:WaitForChild("RemoteInstances") local ToolEvent = RemoteInstance:WaitForChild("toolEvent") local EventHandler = require(REPLICATED_STORAGE:WaitForChild("EventHandler")) local ItemDataFolder = REPLICATED_STORAGE:WaitForChild("ItemData") local LocalPlayer = PLAYER_SERVICE.LocalPlayer or nil --// VARIABLES //-- local toolClasses = {} local toolCache = {} --// LOCAL FUNCTIONS //-- local function init(...) --// Load All Sub Class Tool Modules Into toolClasses local subClasses = script:GetChildren() for i, v in pairs(subClasses) do if not v:IsA("ModuleScript") then continue end toolClasses[v.Name] = require(v) end return end local function createEvent(self, eventName) local newEvent = EventHandler.Event.new(eventName) newEvent:Connect(function(...) if RUN_SERVICE:IsServer() then --// Fire To All Clients ToolEvent:FireAllClients(self.UID, eventName, self.Owner, self.ToolId, self.Model) elseif RUN_SERVICE:IsClient() and self.Owner == LocalPlayer then --// Fire To Server ToolEvent:FireServer(self.UID, eventName, self.Owner, self.ToolId, self.Model) end end) return newEvent end local function onServerEvent(player, ...) for i, v in pairs(PLAYER_SERVICE:GetPlayers()) do if v ~= player then --// Fire Tool Event To Client ToolEvent:FireClient(v, ...) end end end local function onClientEvent(UID, eventName, playerInstance, toolId, toolModel) if eventName == "Created" then ToolClass.new(playerInstance, toolId, UID, toolModel) end local tool = toolCache[UID] if tool then if eventName == "Equipped" then tool:Equip() elseif eventName == "Unequipped" then tool:Unequip() elseif eventName == "Activated" then tool:Activate() elseif eventName == "Deactivated" then tool:Deactivate() elseif eventName == "Destroyed" then tool:Destroy() end else warn("Cache[" .. tostring(UID) .. "] is missing or nil.") end end local function rollTrack(Tracks) return Tracks[math.random(#Tracks)] end --// GLOBAL FUNCTIONS //-- function ToolClass.new(playerInstance, toolId, UID, toolModel) --// Check For Errors if not playerInstance then warn("Arg 1 nil") return end if not toolClasses[toolId] then warn("Arg 2 nil") return end if not UID then warn("Arg 3 nil") return end if not toolModel then warn("Arg 4 nil") return end local self = toolClasses[toolId].new(playerInstance, UID) setmetatable(self, ToolClass) --// Set Default Parameters self.Owner = playerInstance self.ToolId = toolId self.UID = UID self.Model = toolModel --// Create Tool Parameters self.ItemData = require(ItemDataFolder:FindFirstChild(toolId)) or {} self.IsEquipped = false self.Active = false --// Create Events self.Created = createEvent(self, "Created") self.Equipped = createEvent(self, "Equipped") self.Unequipped = createEvent(self, "Unequipped") self.Activated = createEvent(self, "Activated") self.Deactivated = createEvent(self, "Deactivated") self.Destroyed = createEvent(self, "Destroyed") --// Add Tool To Cache toolCache[self.UID] = self --// Fire Created Event self.Created:Fire(self) if toolModel then toolModel:GetPropertyChangedSignal("Parent"):Connect(function() if toolModel.Parent == nil then self:Destroy() end end) end return self end function ToolClass:Equip() if self.Owner then self:UnloadAnimations() end self.Equipped:Fire() self.IsEquipped = true self:LoadAnimations() warn(self.Owner.Name .. " has equipped " .. self.Model.Name) toolClasses[self.ToolId].Equip(self) end function ToolClass:Unequip() self.Unequipped:Fire() self:Deactivate() self.IsEquipped = false warn(self.Owner.Name .. " has unequipped " .. self.Model.Name) toolClasses[self.ToolId].Unequip(self) end function ToolClass:Activate() if not self.IsEquipped then return end self.Active = true self.Activated:Fire() warn(self.Owner.Name .. " has activated " .. self.Model.Name) toolClasses[self.ToolId].Activate(self) end function ToolClass:Deactivate() self.Active = false self.Deactivated:Fire() warn(self.Owner.Name .. " has deactivated " .. self.Model.Name) toolClasses[self.ToolId].Deactivate(self) end function ToolClass:InputBegan(userInput) if not self.IsEquipped then return end warn(" [" .. self.Owner.Name .. "].InputBegan = " .. tostring(userInput)) end function ToolClass:InputEnded(userInput) if not self.IsEquipped then return end warn(" [" .. self.Owner.Name .. "].InputEnded = " .. tostring(userInput)) end function ToolClass:Destroy() toolCache[self.UID] = nil DEBRIS:AddItem(self.Model) self.Destroyed:Fire() toolClasses[self.ToolId].Destroy(self) end function ToolClass:LoadAnimations() local animationController = self.Owner.Character.Humanoid:FindFirstChild("Animator") if not animationController then return end self.AnimationController = animationController if self.Animations == nil then local animations = {} for i, v in pairs(self.ItemData.Animations) do local tracks = {} for i2, v2 in pairs(v) do local animationObject = self.Model:FindFirstChild(i.."_"..tostring(i2)) if not animationObject then animationObject = Instance.new("Animation") animationObject.AnimationId = "rbxassetid://"..v2.Id animationObject.Name = i.."_"..tostring(i2) animationObject.Parent = self.Model end tracks[i2] = { Track = animationController:LoadAnimation(animationObject), Weight = v2.Weight, AnimationObject = animationObject, } end animations[i] = { Tracks = tracks, ActiveTrack = nil } end self.Animations = animations else local AanimationController = self.Owner.Character.Humanoid:FindFirstChild("Animator") for i, Data in pairs(self.Animations) do for i2 = 1, #Data do local TrackData = Data[i2] if TrackData.Track then TrackData.Track:Destroy() end TrackData.Track = animationController:LoadAnimation(TrackData.AnimationObject) end end end end function ToolClass:UnloadAnimations() if self.Animations then for i, v in pairs(self.Animations) do for TrackIndex = 1, #v.Tracks do local TrackData = v.Tracks[TrackIndex] if TrackData.Track then TrackData.Track:Stop() TrackData.Track:Destroy() end end end self.AnimationController = nil end end function ToolClass:StopAnimation(animationName) local animationData = self.Animations[animationName] if animationData and animationData.ActiveTrack then animationData.ActiveTrack:Stop() if RUN_SERVICE:IsClient() and self.Owner == LocalPlayer then ToolEvent:FireServer(self.ID, "StopAnimation", animationName) elseif RUN_SERVICE:IsServer() then ToolEvent:FireAllClients(self.ID, "StopAnimation", animationName) end else warn("Warning: " .. self.ToolId .. ".Animations[" .. tostring(animationName) .. "] is missing or nil.") end end function ToolClass:StopAllAnimations() for i, v in pairs(self.Animations) do if v and v.ActiveTrack then v.ActiveTrack:Stop() end end if RUN_SERVICE:IsClient() and self.Owner == LocalPlayer then ToolEvent:FireServer(self.ID, "StopAllAnimations") elseif RUN_SERVICE:IsServer() then ToolEvent:FireAllClients(self.ID, "StopAllAnimations") end end function ToolClass:PlayAnimation(animationName, isLooped) local animationData = self.Animations[animationName] if animationData then local Data = rollTrack(animationData.Tracks) animationData.ActiveTrack = Data.Track animationData.Looped = isLooped or false animationData.ActiveTrack:Play(nil, Data.Weight) if RUN_SERVICE:IsClient() and self.Owner == LocalPlayer then ToolEvent:FireServer(self.ID, "PlayAnimation", animationName) elseif RUN_SERVICE:IsServer() then ToolEvent:FireAllClients(self.ID, "PlayAnimation", animationName) end return animationData.ActiveTrack else warn("Warning: " .. self.ToolId .. ".Animations[" .. tostring(animationName) .. "] is missing or nil.") end end --// EVENT CONNECTIONS //-- if RUN_SERVICE:IsServer() then ToolEvent.OnServerEvent:Connect(onServerEvent) elseif RUN_SERVICE:IsClient() then ToolEvent.OnClientEvent:Connect(onClientEvent) end --// INIT //-- init() return ToolClass