Review my Gun Syteme

I accept everyone’s opinion! Be tough on me, so I can take into account what I need to improve.

--SUPERCLASS GUN

type Gun = {
	name: string,
	tool: Tool,
	damage: number,
	ammunition: number,
	max_ammunition: number,
	player: Player,
	reloadTime: number,
	reloadState: boolean,
	equippedState: boolean
}

local ContextActionService = game:GetService("ContextActionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--On ReplicatedStorage
local Contents = ReplicatedStorage.Contents
local Events = ReplicatedStorage.Events
--On Contents
local Enums = require(Contents.Enums.Enums)
--On Events
local gunEvent = Events.GunEvent

--Function

local function getInstanceByShootRayCast(self: Gun, mousePosition: Vector3): Instance
	local shootPart = self.tool.Shoot:: BasePart
	local direction = (mousePosition - shootPart.Position).Unit*300
	local result = workspace:Raycast(shootPart.Position, direction)

	if result then
		return result.Instance
	end
end

local function takeDamage(instance: Instance, damage: number)
	local humanoid = instance.Parent:FindFirstChild("Humanoid"):: Humanoid
	if humanoid then
		humanoid:TakeDamage(damage)
	end
end

--Abstract Class
local Gun = {}

function Gun:extends(otherClass): self
	self.__index = self
	
	return setmetatable(otherClass, self)
end

function Gun:onEquipped()
	if self:isEquipped() then return end
	
	self.equippedState = true
	
	gunEvent:FireClient(self.player, Enums.GunEvent.Equipped, self.name, self.ammunition, self.max_ammunition)
end

function Gun:onUnequipped()
	if not self:isEquipped() then return end
	
	self.equippedState = false
	
	gunEvent:FireClient(self.player, Enums.GunEvent.Unequipped)
end

function Gun:shoot(mousePosition: Vector3) -- PRESS MOUSEBUTTON1CLICK
	if self:isReload() then return end
	
	if mousePosition and self.ammunition > 0 then
		local instance = getInstanceByShootRayCast(self, mousePosition)
		
		self.ammunition -= 1
		
		if instance then
			takeDamage(instance, self.damage)
		end

		gunEvent:FireClient(self.player, Enums.GunEvent.Shoot, self.ammunition, self.max_ammunition)
		gunEvent:FireClient(self.player, Enums.GunEvent.Animation)
	end
	
	print(self.ammunition)
end

function Gun:reload() --PRESS R
	if self:isReload() then return end
	
	if self.ammunition < self.max_ammunition then
		self.reloadState = true
		print("In Reloading...")
		task.wait(self.reloadTime)
		print("Reload !")
		self.ammunition = self.max_ammunition
		self.reloadState = false
		
		gunEvent:FireClient(self.player, Enums.GunEvent.Reload, self.ammunition, self.max_ammunition)
	else -- TEMPORAIRE !!!
		print("You cant reload !")
	end
end

function Gun:isEquipped(): boolean
	return self.equippedState
end

function Gun:isReload(): boolean
	if not self:isEquipped() then return end
	
	return self.reloadState
end

export type self = typeof(Gun:extends(...))

return Gun
--SUB CLASS PISTOLET

local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Contents = ServerStorage.Contents
local Package = ReplicatedStorage.Package

local Gun = require(Contents.Gun.Gun)

local Guns = Package.Guns

local subGun = {}

function subGun.new(player: Player)
	local self = setmetatable({}, {
		__index = Gun
	})
	
	self.name = script.Name
	self.tool = Guns:FindFirstChild(script.Name):Clone()
	self.damage = 10
	self.ammunition = 24
	self.max_ammunition = 24
	self.player = player
	self.reloadTime = 2.25
	self.reloadState = false
	self.equippedState = false
	
	Gun:extends(self)
	
	return self
end

return subGun

--Enums module
local Enums = {
	GunEvent = {
		Mouse = "Mouse",
		Shoot = "Shoot",
		Reload = "Reload",
		Animation = "Animation",
		Equipped = "Equipped",
		Unequipped = "Unequipped"
	},
	
	GunAction = {
		ShootAction = "ShootAction",
		ReloadAction = "ReloadAction"
	},
}

export type Enums = typeof(Enums)

return Enums

SCRIPT GUN SERVER
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ServerContents = ServerStorage.Contents
local ReplicatedContents = ReplicatedStorage.Contents
local Guns = ServerContents.Gun.Guns
local Events = ReplicatedStorage.Events

local Player = require(ServerContents.Player.Player)
local Enums = require(ReplicatedContents.Enums.Enums)

local gunEvent = Events.GunEvent

local currentGunObject = nil


local function onGunEvent(player: Player, action: string, arg)
	if action == Enums.GunEvent.Shoot then
		currentGunObject:shoot(arg)
	elseif action == Enums.GunEvent.Reload then
		currentGunObject:reload()
	end
end

--Players.PlayerAdded:Connect(onPlayerAdded)
gunEvent.OnServerEvent:Connect(onGunEvent)
Client GUN


if not game:IsLoaded() then game.Loaded:Wait() end
local ContextActionService = game:GetService("ContextActionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Contents = ReplicatedStorage.Contents
local Events = ReplicatedStorage.Events
local Package = ReplicatedStorage.Package

local Enums = require(Contents.Enums.Enums)

local Guns = Package.Guns

local gunEvent = Events.GunEvent

local player = game.Players.LocalPlayer
local playerGui = player.PlayerGui
local mouse = player:GetMouse()
local character = player.Character
local humanoid = character.Humanoid:: Humanoid
local animator = humanoid.Animator:: Animator

local container = playerGui.PlayerGui.Container
local gunFrame = container.Gun
local viewPortFrame = gunFrame.ViewportFrame
local gunName = gunFrame.GunName
local ammunitionText = gunFrame.Ammunition
local viewPortCamera = Instance.new("Camera")

local shootAnimation = script.Shoot

local canPlayAnimation = true

viewPortCamera.Parent = viewPortFrame

local function playAnimation(animation: Animation, waitAnimationCompleted: boolean?)
	if not canPlayAnimation then return end
	
	local animationTrack = animator:LoadAnimation(animation):: AnimationTrack
	animationTrack.Priority = Enum.AnimationPriority.Action
	
	animationTrack:Play()
	canPlayAnimation = false

	if waitAnimationCompleted then
		animationTrack.Stopped:Wait()
		animationTrack:Stop()
	end
	canPlayAnimation = true
end

local function getMousePosition()
	return mouse.Hit.Position
end

local function setViewPortFrame(handle)
	viewPortCamera.CFrame = CFrame.new(Vector3.zero) * CFrame.Angles(0, 0, 0)
	handle.CFrame = CFrame.new(Vector3.zero) * CFrame.Angles(0, math.rad(90), 0)
	
	viewPortCamera.CameraSubject = handle
end

local function setGunName(name: string)
	gunName.Text = name
end

local function setGunAmmunition(ammunition: number, maxAmmunition: number)
	ammunitionText.Text = ammunition.."/"..maxAmmunition
end

local function onShoot(actionName, userInputState)
	if userInputState == Enum.UserInputState.Begin and actionName == Enums.GunAction.ShootAction then
		gunEvent:FireServer(Enums.GunEvent.Shoot, getMousePosition())
	end
end

local function onReload(actionName, userInputState)
	if userInputState == Enum.UserInputState.Begin and actionName == Enums.GunAction.ReloadAction then
		gunEvent:FireServer(Enums.GunEvent.Reload)
	end
end

local function setGunInfo(name: string, ammunition: number, maxAmmunition: number)
	local gunTool = Guns:FindFirstChild(name)
	
	if not gunTool then return end
	
	local cloneHandle = gunTool:FindFirstChild("Handle"):Clone():: BasePart
	
	gunFrame.Visible = true
	cloneHandle.Parent = viewPortFrame
	
	setViewPortFrame(cloneHandle)
	setGunName(name)
	setGunAmmunition(ammunition, maxAmmunition)
end

local function removeGunInfo()
	local handle = viewPortFrame:FindFirstChild("Handle")
	
	gunFrame.Visible = false
	
	viewPortCamera.CameraSubject = nil
	gunName.Text = "NIL"
	ammunitionText.Text = "NIL"
	
	if handle then
		handle:Destroy()
	end
end

local function setBindsAction()
	ContextActionService:BindAction(Enums.GunAction.ShootAction, onShoot, true, Enum.UserInputType.MouseButton1)
	ContextActionService:BindAction(Enums.GunAction.ReloadAction, onReload, true, Enum.KeyCode.R)
end

local function removeBindsAction()
	ContextActionService:UnbindAction(Enums.GunAction.ShootAction)
	ContextActionService:UnbindAction(Enums.GunAction.ReloadAction)
end

gunEvent.OnClientEvent:Connect(function(action, ...)
	if action == Enums.GunEvent.Animation then
		playAnimation(shootAnimation, true)
	elseif action == Enums.GunEvent.Equipped then
		print(...)
		setGunInfo(...)
		setBindsAction()
	elseif action == Enums.GunEvent.Unequipped then
		removeGunInfo()
		removeBindsAction()
	elseif action == Enums.GunEvent.Shoot or action ==  Enums.GunEvent.Reload then
		setGunAmmunition(...)
	end
end)
2 Likes

You could add more complex stuff like reloading, bullet spread, bullet drop, if its a shotgun then shoot pellets (multiple bullets instead of one), or also fire types (semi-auto, semi, burst, etc). Looks good to me.

4 Likes

Looks clean and readable to me :+1:

2 Likes