I messed up.
I’ve been developing this simple gun framework for a game, but the problem is when inherriting methods from the base-class, which I wasn’t aware of at the time, are called in the same module.
This is problematic for me since I assumed if I create a sub-class I could just change some values, but the module isn’t aware of these modifications and uses the default values.
Two problems are noticable. Firstly, the module can’t require the correct server module. Second I can’t add or change firing modes such as adding auto, burst, ect. becaue changes to the table aren’t accessible and even if I found a way around this, the module will not be able to call the functions.
Should I re-factor into composition? Is there a way this can be salvaged? I want suggestions…
VVV [ CODE ] VVV
BaseClass
This is run by a local script that connects equip and unequip to a tool and keys to CAS
--ClientModule
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local Auxillary = require(ReplicatedStorage.Modules.Auxillary.Auxillary)
local BetterRobloxAPI = require(ReplicatedStorage.Modules.BetterRobloxAPI)
local actionService = ReplicatedStorage.Remotes.ActionService
local replicateRemote = ReplicatedStorage.Remotes.Replicate
local assets = ReplicatedFirst.Assets.Gun
local sound = assets.Sound
local casterParams = RaycastParams.new()
local function isMouseButtonDown(inputType)
for _, button in UserInputService:GetMouseButtonsPressed() do
if button.UserInputType == inputType then
return true
end
end
return false
end
local BASE_GUN = {}
BASE_GUN.__index = BASE_GUN
----[{STATS}]----
BASE_GUN.reloadTime = 1
BASE_GUN.firingModes = {
"Semi"
}
BASE_GUN.timeBetweenShots = 0.02
BASE_GUN.magazineCompacity = 15-- Max amount of bullets in a mag
BASE_GUN.maxAmmo = 96
----[[=====]]----
BASE_GUN.Keys = {
["Click"] = {["Name"] = "Fire",
["Inputs"] = {Enum.UserInputType.MouseButton1},
},
["R"] = {["Name"] = "Reload",
["Inputs"] = {Enum.KeyCode.R},
},
["C"] = {["Name"] = "Crouch",
["Inputs"] = {Enum.KeyCode.C},
},
["Q"] = {["Name"] = "PeakLeft",
["Inputs"] = {Enum.KeyCode.Q},
},
["E"] = {["Name"] = "PeakRight",
["Inputs"] = {Enum.KeyCode.E},
},
["V"] = {["Name"] = "FiringMode",
["Inputs"] = {Enum.KeyCode.V},
},
}
function BASE_GUN.new(User:Player, AuxObjects, tool:Tool)
local self = setmetatable({
Janitor = Janitor.new()
}, BASE_GUN)
self.user = User
self.character = User.character
self.tool = tool
self.mouse = User:GetMouse()
self.CameraShaker = AuxObjects.CameraShaker
self.StatusManager = AuxObjects.StatusManager
self.spareAmmo = BASE_GUN.maxAmmo--How much of the max ammo did we start out with is left
self.firingMode = BASE_GUN.firingModes[1]
self.bulletsLeft = 0
self.timeLastFired = os.clock()
self.Disabled = false
self.Equipped = false
return self
end
--Equipping
function BASE_GUN:Equip()
local serverArgs = {
["Module"] = script.Name,
["Action"] = debug.info(1, "n"),
["Tool"] = self.tool,
}
actionService:InvokeServer(serverArgs)
self.Equipped = true
end
function BASE_GUN:Unequip()
self.Equipped = false
end
function BASE_GUN:FiringMode()
if
self.tool.Parent == self.character --gaurd check just to be sure the clients not screwing us over
and self.Equipped
and not self.Disabled
--and not self.StatusManager:GetStatus().Stun
then
local numb = self.firingMode
local serverArgs = {
["Module"] = script.Name,
["Action"] = debug.info(1, "n"),
["Index"] = numb,
}
self.firingMode = actionService:InvokeServer(serverArgs)
end
end
--Fireing
function BASE_GUN:Fire()
if
self.tool.Parent == self.character --gaurd check just to be sure the clients not screwing us over
and self.Equipped
and not self.Disabled
and os.clock() - self.timeLastFired >= BASE_GUN.timeBetweenShots --Debounce
--and not self.StatusManager:GetStatus().Stun
then
if self.bulletsLeft <= 0 then
self:Reload(true)
return
end
print(self.firingMode, self.timeLastFired, self.Disabled)
self[self.firingMode.."Fire"](self)
return
end
end
function BASE_GUN:_ShootBullet() -- Handles the anoying math that needs to be concualted to fire and subtract ammo
if
not self.Disabled
then
if self.bulletsLeft <= 0 then -- Do we REALLY need reloading if we can just disable the BASE_GUN?
--TODO play sound to signify there are no more rounds left
self:Reload(true)
return
end
local serverArgs = {
["Module"] = script.Name,
["Action"] = debug.info(3, "n"),
["Tool"] = self.tool,
["Hit"] = self.mouse.Hit.Position,
}
local callback = actionService:InvokeServer(serverArgs)
--wait for callback
self.bulletsLeft -= 1
self.timeLastFired = os.clock()
self.tool.Name = string.format("M9 [%i][%i] ", self.spareAmmo, self.bulletsLeft)
end
end
function BASE_GUN:SemiFire()
while isMouseButtonDown(Enum.UserInputType.MouseButton1 or Enum.UserInputType.Touch or Enum.KeyCode.ButtonR2) do
if os.clock() - self.timeLastFired >= BASE_GUN.timeBetweenShots then
self:_ShootBullet()
game["Run Service"].Stepped:Wait()
end
end
end
--Reloading
function BASE_GUN:Reload(noBulletsLeft)
if
self.tool.Parent == self.character --gaurd check just to be sure the clients not screwing us over
and self.Equipped
and not self.Disabled
and self.bulletsLeft ~= BASE_GUN.magazineCompacity
and self.spareAmmo > 0
then
self.Disabled = true
self.tool.Name = string.format("M9 [%s] ", "reloading...")
local serverArgs = {
["Module"] = script.Name,
["Action"] = debug.info(1, "n"),
["ReloadTime"] = BASE_GUN.reloadTime,
["Tool"] = self.tool
}
if noBulletsLeft then
local callback = actionService:InvokeServer(
{
["Module"] = script.Name,
["Action"] = "EjectCassing",
["Tool"] = self.tool
})
end
local callback = actionService:InvokeServer(serverArgs)
--wait for callback
local result = math.min(BASE_GUN.magazineCompacity - self.bulletsLeft, self.spareAmmo)
self.spareAmmo -= result
self.bulletsLeft += result
self.Disabled = false
self.tool.Name = string.format("M9 [%i][%i] ", self.spareAmmo, self.bulletsLeft)
return
end
end
function BASE_GUN:ReloadEnd()
return
end
--Fired
function BASE_GUN:FireEnd()
return
end
function BASE_GUN:FiringModeEnd()
return
end
--Tool Functionsv
function BASE_GUN:Disable()
self.Disabled = false
end
function BASE_GUN:Enable()
self.Disabled = true
end
function BASE_GUN:Destroy()
return pcall(function()
--self.Janitor
setmetatable(self, nil)
table.clear(self)
end)
end
return table.freeze(BASE_GUN)
ㅤ
ㅤ
ㅤ
ㅤ
ㅤ
InheritExample
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local Janitor = require(ReplicatedStorage.Modules.Janitor)
local Auxillary = require(ReplicatedStorage.Modules.Auxillary.Auxillary)
local BetterRobloxAPI = require(ReplicatedStorage.Modules.BetterRobloxAPI)
local BASE_GUN = require(script.Parent)
local actionService = ReplicatedStorage.Remotes.ActionService
local replicateRemote = ReplicatedStorage.Remotes.Replicate
local assets = ReplicatedFirst.Assets.M16
local VFXs = assets.VFX
local sound = assets.Sound
local casterParams = RaycastParams.new()
local function isMouseButtonDown(inputType)
for _, button in UserInputService:GetMouseButtonsPressed() do
if button.UserInputType == inputType then
return true
end
end
return false
end
local M16 = {}
setmetatable(M16, {__index = BASE_GUN})
----[{STATS}]----
M16.reloadTime = 1
M16.firingModes = {
"Semi",
"Burst",
}
M16.timeBetweenShots = 1
M16.magazineCompacity = 30-- Max amount of bullets in a mag
M16.maxAmmo = 200
----[[=====]]----
M16.Keys = {
["Click"] = {["Name"] = "Fire",
["Inputs"] = {Enum.UserInputType.MouseButton1},
},
["R"] = {["Name"] = "Reload",
["Inputs"] = {Enum.KeyCode.R},
},
["C"] = {["Name"] = "Crouch",
["Inputs"] = {Enum.KeyCode.C},
},
["Q"] = {["Name"] = "PeakLeft",
["Inputs"] = {Enum.KeyCode.Q},
},
["E"] = {["Name"] = "PeakRight",
["Inputs"] = {Enum.KeyCode.E},
},
["V"] = {["Name"] = "FiringMode",
["Inputs"] = {Enum.KeyCode.V},
},
}
function M16.new(User:Player, AuxObjects, tool:Tool)
local self = setmetatable(BASE_GUN.new(User, AuxObjects, tool), {__index = M16})
return self
end
function M16:BurstFire()
while isMouseButtonDown(Enum.UserInputType.MouseButton1 or Enum.UserInputType.Touch or Enum.KeyCode.ButtonR2) do
if os.clock() - self.timeLastFired >= M16.timeBetweenShots then
self:_ShootBullet()
game["Run Service"].Stepped:Wait()
self:_ShootBullet()
game["Run Service"].Stepped:Wait()
self:_ShootBullet()
game["Run Service"].Stepped:Wait()
end
end
end
return table.freeze(M16)
ㅤ
ㅤ
ㅤ
ㅤ
ㅤ