I am trying to make a script to recognize when any player’s tool is activated without storing individual scripts within each tool.
I’m trying to make an OOP weapons system.
My code currently looks something like this:
local weaponsFolder = game.ServerStorage.WeaponsFolder --This contains weapons which are cloned.
local Weapon = {}
Weapon.toolInstances = {}--This holds all created weapon instances
Weapon.__index = Weapon
function Weapon.new(player)
local self = setmetatable({},Weapon)
self.Player = player
self.Tool = weaponsFolder.Tool:Clone()
self.Tool.Parent = player.Backpack
table.insert(Weapon.toolInstances,self.Tool)
return self
end
function Weapon:Attack()--This function will be called upon the tool's activation
end
return Weapon
I have tried adding each created tool instance to a table and running it in a for loop like this in separate places (A server script, this module script, and a different module script).
for i,v in ipairs(toolInstances) do
toolInstances[i].Activated:Connect(WeaponModule:Attack())
i += 1
end
Everytime a player would leave or join the game this for loop would be rerun since the toolInstances table changes, but this approach has resulted in many errors.
I’d like to know if there is a better way to handle this, if you’ve got any suggestions I’d be glad to hear them.
It’d be a bit complex to setup but definitely worth doing, you create a module script with the same name as the tool then require it whenever it’s equipped by the player for the first time to connect other events
for example you have a Script called ToolService
ToolService handles Tool distribution and listens to events
something like this
function ToolService.giveToolToPlayer(name: string, player: Player)
-- find tool, if found then
-- find ModuleScript, if found then require
-- connect equipped, activated and unequipped event
-- find player.Backpack, if found then give tool to player
end
local ToolService = {}
local Tools = {}
local function registerTools()
for _, tool: Tool in script.Tools:GetChildren() do
local modulescript: ModuleScript? = script.ModuleScripts:FindFirstChild(tool.Name)
if not modulescript then
warn(`Can not find valid ModuleScript for Tool {tool.Name}`)
continue
end
Tools[tool.Name] = {Tool = tool, ModuleScript = require(modulescript)}
end
end
registerTools()
local EVENTS = {
"Activated",
"Equipped",
"Deactivated",
"Destroying",
"Unequipped",
}
function ToolService.giveToolToPlayer(name: string, player: Player)
local tooldata = Tools[name]
if not tooldata then
warn(`Can not find Tool with name {name}`)
return
else
local backpack = player:WaitForChild("Backpack")
local tool: Tool = tooldata.Tool:Clone()
local toolScript = tooldata.ModuleScript
for _, event: string in EVENTS do
local callback = toolScript[event]
if callback then
tool[event]:Connect(toolScript.callback)
end
end
tool.Parent = backpack
end
end
return ToolService
You can simply connect to it’s events as you would normally with an ‘instantiator’, an function that redirects function calls to the main method. For example:
local Sword = {}
-- Main method
function Sword:Equipped(Tool)
print(`Hello from tool {Tool.Name} being equipped.`)
end
-- Connecting it to the tool in specific
local ThisTool = Sword
local ToolObject = Instance.new("Tool")
SwordTool.Equipped:Connect(function()
ThisTool:Equipped(ToolObject)
end)
Making it better:
local Sword = {}
-- You could inherit some of this tool's methods from a parent class, like BaseTool, but i'll not for this simple example.
function Sword.new(Tool)
Tool.Equipped:Connect(function()
Sword:Equipped(Tool)
end)
return Tool
end
function Sword:Equipped(Tool)
print(`Hello from sword tool {Tool.Name} being equipped.`)
end
-- Removed manual connections, now we can simply instantiate it from its origin and let the .new function deal with all connections and instantiation itself.
local SwordTool = Sword.new(ToolObjectHere)
-- Examples:
local BladeSword = {}
BladeSword.ToolObject = Tool_Here
function BladeSword:Create()
return Sword.new(self.ToolObject:Clone())
end
game.Players.PlayerAdded:Connect(function(Player)
local Backpack = Player:WaitForChild('Backpack')
local this = BladeSword:Create()
this.Parent = Backpack
end)
Added a .new function to the class Sword and examples. Now, it handles all instantiation and connections itself. Removing unnecessary headache.
OOP doesn’t add ‘unnecessary complications to your codebase’; it prevents people, like you, from creating a mess in the form of a script, as you did.
Your code is way too specific. What if he or someone wants only to instantiate the tool itself, rather than placing it on the player’s backpack directly?
Use a module! and use remote functions.
Here is a simple example of a remote function.
You would use this so that all of the variables are stored and excuted on one script.
simple implementation could be.
This has the advantages of handling all of the tools in one script but disadvantage of running them all on one thread. this also allows for server scripts to communicate between each other
toolfunctions={["Sword"]=function(Character)
--all the code for the sword with its local variables and functions
return tool
end}
local remoteFunction = workspace.GlobalSpells.RemoteFunction -- Find the same RemoteFunction object in the Workspace
remoteFunction.OnServerInvoke = function(name) -- Define a function that runs when the RemoteFunction is invoked on the server and receives the name as a parameter
--local func= toolfunctions{"Sword"}
func=toolfunctions{name}
return func(Character) --Return the local function connections for the tool
end
Besides the fact that a basic OOP implementation already allows for this; You just need to serialize tools from instances to classes, internally.
For example:
Tool Instance
Handle: Part
Instantiator: Module
This module should be taken from the Tool after instance serialization, obviously.
Module example:
local Sword = require()
-- Path to the tool class Sword.
local BladeSword = {}
local ToolObject = script.Parent
function BladeSword:Create(Name)
local this = Sword.new(Toolbject)
this.Name = Name
return this
end
BladeSword.ToolObject = ToolObject
return BladeSword
Not exactly. Coding is a tool to get the job done and to maintain it. If you’re developing something that will be used by other people or that you want to be reading later, keeping it clean and organized is essential. As i mentioned, based off my opinions and standards, your code isn’t good.
If we need to alter your code for it to do what we want, it’s a clear signal that it shouldn’t be used; For what i can say is, it’s too specific. Doesn’t suit perfect use cases.
Just like i said previously, there’s an single function holding all of the entire code’s logic; Some of the tasks it does could be divided into smaller functions to guarantee usability and maintanability, but they are not.
Could you explain more about ‘your’ industry standards and what you do to follow them? I’d love to hear more about your “professionalism”