I am pretty amateur at scripting and only recently, I’ve tried to really utilize OOP and single script architecture in my projects. I’ve been stumped by this cyclic dependency issue and have spent some time thinking of ways to resolve it:
-
Create a helper script for each class (to do functions like GetPlayer, etc) and have the script that needs to access the playerClass access this helper class instead, although this helper class also requires access to the class itself, so that doesn’t help
-
Add an attribute to each player that joined, that contains their player class info
If someone can tell me of a way to resolve this, and explains their thoughts, I would really appreciate it! Thanks.
-- player
local PlayerClass = {}
PlayerClass.__index = PlayerClass
local replicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local playersService = game:GetService("Players")
local DebrisService = game:GetService("Debris")
local ragdollService = require(replicatedStorage.RagdollService)
local GunClass = require(ServerScriptService.Server.GunServer)
local RemoteEvents = replicatedStorage.RemoteEvents
local InitializedPlayers = {}
playersService.PlayerAdded:Connect(function(plr)
PlayerClass.new(plr)
end)
function PlayerClass.GetAllGuns()
local tbl = {}
for _,v in InitializedPlayers do
table.insert(tbl, v.GetGun())
end
return tbl
end
function PlayerClass.GetPlayer(playerName)
return InitializedPlayers[playerName]
end
function PlayerClass.new(player: Player)
local self = setmetatable({}, PlayerClass)
self.Player = player
self.IsGunEquipped = false
self.Gun = GunClass.new(self, "TROY DEFENSE AR")
player.CharacterAdded:Connect(function(char)
self:OnCharacterAdded(char)
end)
RemoteEvents.EquipGun.OnServerEvent:Connect(function(plr, IsEquipped)
if plr ~= self.Player then return end
self.IsGunEquipped = IsEquipped
end)
RemoteEvents.Fire.OnServerEvent:Connect(function(plr, startPos, dir)
if plr ~= self.Player then return end
self.Gun:Fire(PlayerClass.GetAllGuns(), startPos, dir)
end)
InitializedPlayers[player.Name] = self
return self
end
function PlayerClass:OnCharacterAdded(character)
self.Gun:WeldToCharacter(character)
self.IsGunEquipped = false
local humanoid = character:WaitForChild("Humanoid")
humanoid.BreakJointsOnDeath = false
humanoid.Died:Connect(function()
self:Death()
end)
end
function PlayerClass:FireGun(startPos, dir)
end
function PlayerClass:GetGun()
return self.Player.Character:FindFirstChildOfClass("Model")
end
function PlayerClass:Death()
local character = self.Player.Character
character.Archivable = true
local bulletDirection = character:GetAttribute("bulletDirection")
ragdollService.Ragdoll(character, self.IsGunEquipped, bulletDirection)
end
function PlayerClass:TakeDamage(damageAmount, bulletDirection)
local character = self.Player.Character
character:SetAttribute("bulletDirection", bulletDirection)
task.delay(2,function()
character:SetAttribute("bulletDirection", nil)
end)
local humanoid = character.Humanoid
humanoid.Health -= damageAmount
end
export type PlayerClass = {
Player: Player,
IsGunEquipped: boolean
}
return PlayerClass
-- gun class
local GunClass = {}
GunClass.__index = GunClass
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlayerClass = require(ServerScriptService.Server.PlayerClass)
function GunClass.new(initializedPlayer, gunName)
local configs = require(ReplicatedStorage:FindFirstChild(gunName))
if not configs then error("Configs not found for " .. gunName) end
local self = setmetatable({}, GunClass)
self.Player = initializedPlayer
self.CurrentAmmo = configs.MaxAmmo
self.MaxAmmo = configs.MaxAmmo
self.Configs = configs
return self
end
function GunClass:Fire(filters, startPos, dir)
if self.CurrentAmmo <= 0 then return end
self.CurrentAmmo -= 1
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params:AddToFilter(filters)
local raycast = workspace:Raycast(startPos, dir * 1000, params)
if raycast and raycast.Instance then
if not raycast.Instance.Parent:FindFirstChild("Humanoid") then return end
PlayerClass.GetPlayer(raycast.Instance.Parent.Name):TakeDamage(101, dir.Unit)
end
end
function GunClass:Reload()
self.CurrentAmmo = self.MaxAmmo
end
function GunClass:WeldToCharacter(character)
local weld = self.Configs.Weld:Clone()
local model = self.Configs.Model:Clone()
model.Parent = character
weld.Parent = character.Torso
weld.Part0 = character.Torso
weld.Part1 = model.Holder
end
return GunClass