G’day, I’m looking for feedback on what I can do to improve this script, it will bug out occasionally but it is functional. Improving speed/ reducing lag would also be useful. It is the local core for a weapon system.
local PlayerService = game:GetService("Players")
local RStorage = game:GetService("ReplicatedStorage")
local UserInputS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local MarketPlace = game:GetService("MarketplaceService")
local animationData = RStorage:WaitForChild("Animations")
local Events = RStorage:WaitForChild("Events")
local RenderReq = RStorage:WaitForChild("Render")
local gunDataModule = require(RStorage:WaitForChild("GunData"))
local gunData = gunDataModule.Weaponry
local gunDataFunctions = gunDataModule.Functions
local LocalPlayer = PlayerService.LocalPlayer
local Character = nil
local Humanoid = nil
local loadedAnimations = {}
local registeredWeapons = {}
local equippedWeapon = nil
local Equipped = false
local equippedWeaponInfo = {}
local equippedWeaponAnimations = {}
local equippedWeaponRegister = {}
local mouseDown = false
local canFire = true
local isFiring = false
local Reloading = false
local isHolstering = false
local weaponGUI = nil
local processedFirstChar = false
local redCursor = "rbxassetid://131718495"
local whiteCursor = "rbxassetid://131581677"
local greenCursor = "rbxassetid://131718487"
local pool = require(RStorage:WaitForChild("ClientUtil", 30):WaitForChild("ObjectPool", 30)).new(250)
local GunClient={
['trailPool'] = pool;
['timeout'] = {};
['bullets'] = {};
['returnPool'] = {};
}
function loadAnimation(AnimationObj)
if not Character then return end
if loadedAnimations[AnimationObj] then return loadedAnimations[AnimationObj] end
local loadedAnimation = Humanoid:LoadAnimation(AnimationObj)
loadedAnimations[AnimationObj] = loadedAnimation
end
function newRayCast(TargetCFrame)
local ray = Ray.new(Character.Torso.CFrame.p, (TargetCFrame.p - Character.Torso.CFrame.p).unit * 300)
return ray
end
function createRay(targetPosition, overrideInfo,hitPart)
overrideInfo = overrideInfo or {}
local rayDistance = ((overrideInfo.Origin or equippedWeapon.Barrel.CFrame.p) - targetPosition).magnitude
if rayDistance > 5000 then
rayDistance = 500
end
local a1 = Instance.new("Attachment")
local a2 = Instance.new("Attachment")
local trail = GunClient.trailPool:GetInstance("Trail", false, true)
trail.Enabled = true
local color = nil
if overrideInfo.Color then
color = ColorSequence.new(overrideInfo.Color.Color)
else
color = ColorSequence.new(equippedWeaponInfo.BulletColor.Color)
end
trail.Color = color
trail.Attachment0 = a1
trail.Attachment1 = a2
a1.Position = targetPosition
a2.Position = targetPosition
a1.Parent = workspace.Terrain
a2.Parent = workspace.Terrain
trail.Parent = workspace.Terrain
table.insert(GunClient.bullets, {
t = trail,
a1 = a1,
a2 = a2,
l = ((overrideInfo.Origin or equippedWeapon.Barrel.CFrame.p) - targetPosition).magnitude,
dir = CFrame.new((overrideInfo.Origin or equippedWeapon.Barrel.CFrame.p), targetPosition),
s = 0
})
end
function getPartOnRay(ray)
if ray then
return workspace:FindPartOnRay(ray, LocalPlayer.Character, false, true)
end
end
function updateCursor(mouse)
if displayHit then
weaponGUI.Hit.Position = UDim2.new(0,mouse.X-16,0,mouse.Y-16)
end
if mouse.Target then
local hum = getHumanoid(mouse.Target)
if hum then
local Player = PlayerService:GetPlayerFromCharacter(hum.Parent)
if Player then
if Player.Team == LocalPlayer.Team then
mouse.Icon = greenCursor
else
mouse.Icon = redCursor
end
else
mouse.Icon = redCursor
end
else
mouse.Icon = whiteCursor
end
end
end
function getHumanoid(Object)
if not Object then return end
if Object.Parent:FindFirstChild("Humanoid") then
return Object.Parent.Humanoid
elseif Object.Parent.Parent:FindFirstChild("Humanoid") then
return Object.Parent.Parent.Humanoid
end
end
function animateCharacter(animationname)
if equippedWeaponRegister and equippedWeaponRegister.Animations[animationname] then
equippedWeaponRegister.Animations[animationname]:Play()
end
end
function StopSpecAni(animationName)
if equippedWeaponRegister and equippedWeaponRegister.Animations[animationName] then
equippedWeaponRegister.Animations[animationName]:Stop()
end
end
function StopWepAni()
if not equippedWeaponRegister or not equippedWeaponRegister.Animations then return end
for i,v in pairs(equippedWeaponRegister.Animations) do
v:Stop()
end
end
function playSound(soundname)
if equippedWeaponRegister and equippedWeaponRegister.Sounds[soundname] then
equippedWeaponRegister.Sounds[soundname]:Play()
end
end
function runWeapon(newRunVal)
if newRunVal then
if Reloading or isFiring then return end
isHolstering = true
StopSpecAni("Idle")
animateCharacter("Run")
Humanoid.WalkSpeed = 24
else
isHolstering = false
animateCharacter("Idle")
StopSpecAni("Run")
Humanoid.WalkSpeed = 16
end
end
function UpdateWepGUI()
if equippedWeaponRegister and equippedWeaponRegister.Ammo then
weaponGUI.Main.Ammo.Text = equippedWeaponRegister.Ammo
weaponGUI.Main.FiringMode.Text = "Firing mode: " .. equippedWeaponRegister.weaponType
end
end
function shoot(mouse)
canFire = false
if equippedWeapon and Equipped then
local targetPos = mouse.Hit.p
local rayDistance = (equippedWeapon.Handle.Position - targetPos).magnitude
local aimSpread = Vector3.new((targetPos.x)+(math.random(-(equippedWeaponInfo.BulletSpread/15)*rayDistance,(equippedWeaponInfo.BulletSpread/15)*rayDistance)),(targetPos.y)+(math.random(-(equippedWeaponInfo.BulletSpread/15)*rayDistance,(equippedWeaponInfo.BulletSpread/15)*rayDistance)),(targetPos.z)+(math.random(-(equippedWeaponInfo.BulletSpread/15)*rayDistance,(equippedWeaponInfo.BulletSpread/15)*rayDistance)))
local ray = newRayCast(CFrame.new(aimSpread))
local hitPart,hitPosition = getPartOnRay(ray)
local visualRay = createRay(hitPosition)
if hitPart then
local Humanoid = getHumanoid(hitPart)
if Humanoid then
weaponGUI.Hit.Visible = true
displayHit = true
weaponGUI.hitSound:Play()
weaponGUI.Hit.Position = UDim2.new(0,mouse.X-16,0,mouse.Y-16)
delay(0.1, function() displayHit = false weaponGUI.Hit.Visible = false end)
end
end
Events.GunFire:FireServer(equippedWeapon, hitPart, equippedWeapon.Barrel.Position, targetPos, equippedWeaponInfo.BulletColor)
end
spawn(function()
equippedWeapon.Barrel.Flash.Enabled = true
wait(0.1)
equippedWeapon.Barrel.Flash.Enabled = false
end)
equippedWeaponRegister.Ammo = equippedWeaponRegister.Ammo - 1
UpdateWepGUI()
wait(equippedWeaponInfo.FireRate)
canFire = true
end
function reload(Weapon)
if Reloading then return end
if isHolstering then
runWeapon(false)
end
canFire = false
Reloading = true
animateCharacter("Reload")
playSound("Reload")
spawn(function()
for i=1,3,1 do
weaponGUI.Main.Ammo.Text = string.rep(".", i)
if equippedWeaponRegister.Animations then
wait(equippedWeaponRegister.Animations.Reload.Length/3)
end
end
end)
wait(equippedWeaponRegister.Animations.Reload.Length)
if not equippedWeapon == Weapon then return end
equippedWeaponRegister.Ammo = equippedWeaponInfo.MaxAmmo
UpdateWepGUI()
Reloading = false
canFire = true
end
function fireloop(mouse, Weapon)
if not canFire or not Equipped or not equippedWeapon == Weapon or Reloading or Humanoid.Health <= 0 then return end
if mouseDown then
if equippedWeaponInfo.Wait then
warn("Waiting for weapon,", equippedWeaponInfo.Wait)
playSound("Wind")
wait(equippedWeaponInfo.Wait)
end
if isHolstering then
runWeapon(false)
end
if equippedWeaponRegister.weaponType == "Auto" then
while mouseDown and Equipped and equippedWeapon == Weapon and equippedWeaponRegister.Ammo > 0 and canFire and not Reloading and Humanoid.Health > 0 do
isFiring = true
shoot(mouse)
isFiring = false
end
elseif equippedWeaponRegister.weaponType == "Semi" and equippedWeaponRegister.Ammo > 0 then
isFiring = true
shoot(mouse)
isFiring = false
end
if equippedWeaponRegister.Ammo <= 0 then
playSound("EmptyMag")
reload(Weapon)
end
end
end
function equip(mouse, Weapon)
if not registeredWeapons[Weapon] then warn("Akai: This weapon is Unregistered") return end
if Humanoid.Health <= 0 then return end
equippedWeapon = Weapon
equippedWeaponInfo = registeredWeapons[Weapon].weaponInfo
equippedWeaponRegister = registeredWeapons[Weapon]
Equipped = true
weaponGUI.Main._.Border.BackgroundColor3 = equippedWeaponInfo.BulletColor.Color
weaponGUI.Main._.Border2.BackgroundColor3 = equippedWeaponInfo.BulletColor.Color
weaponGUI.Main.WeaponName.Text = equippedWeapon.Name
weaponGUI.Main.Visible = true
UpdateWepGUI()
if not equippedWeaponRegister.Animations["Idle"] then
local specificAnimations = gunDataFunctions.GetSpecificAnimations(equippedWeaponInfo.SpecificType)
for i,v in pairs(specificAnimations) do
local loadedAnimation = loadAnimation(v)
equippedWeaponRegister.Animations[v.Name] = loadedAnimation
end
end
if not equippedWeaponRegister.Sounds["Fire"] then
for i,v in pairs(Weapon.Handle:GetChildren()) do
if v:IsA("Sound") then
equippedWeaponRegister.Sounds[v.Name] = v
end
end
end
animateCharacter("Idle")
mouse.Button1Down:Connect(function()
mouseDown = true
if equippedWeaponRegister.Warmup then
wait(equippedWeaponRegister.Warmup)
end
fireloop(mouse, Weapon)
end)
mouse.Button1Up:Connect(function()
mouseDown = false
end)
mouse.Move:Connect(function()
updateCursor(mouse)
end)
end
function unEquip()
if Humanoid.Health <= 0 then return end
Humanoid.WalkSpeed = 16
StopWepAni()
Equipped = false
equippedWeapon = nil
equippedWeaponInfo = {}
equippedWeaponRegister = {}
weaponGUI.Main.Visible = false
end
function register(Weapon)
if registeredWeapons[Weapon] then return end
local localWeaponInfo = gunData[Weapon.Name]
if localWeaponInfo then
Weapon:WaitForChild("Barrel"):WaitForChild("Flash").Color = ColorSequence.new(localWeaponInfo.BulletColor.Color)
local equippedConnection = Weapon.Equipped:Connect(function(mouse)
equip(mouse, Weapon)
end)
local unequipConnection = Weapon.Unequipped:Connect(unEquip)
registeredWeapons[Weapon] = {
["equippedConnection"] = equippedConnection;
["unequippedConnection"] = unequipConnection;
["Ammo"] = localWeaponInfo.MaxAmmo;
["weaponInfo"] = localWeaponInfo;
["weaponType"] = localWeaponInfo.WeaponType;
["Animations"] = {};
["Sounds"] = {};
}
else
warn("Akai: Weapon unable to register: " .. Weapon.Name .. ", no reference in GunData.")
end
end
local ChatEnabled = true
function keyDown(Key)
if Key == Enum.KeyCode["Z"] and not Reloading then
ChatEnabled = not ChatEnabled
game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.Chat, ChatEnabled)
elseif Key == Enum.KeyCode["R"] and not Reloading then
if Equipped and equippedWeaponRegister.Ammo < equippedWeaponInfo.MaxAmmo then
reload(equippedWeapon)
end
elseif Key == Enum.KeyCode["V"] and not Reloading and not isFiring then
if Equipped and equippedWeaponInfo.SpecificType:lower():match("auto") then
if equippedWeaponRegister.weaponType == "Auto" then
animateCharacter("Switch")
equippedWeaponRegister.weaponType = "Semi"
elseif equippedWeaponRegister.weaponTypr == "Semi" then
animateCharacter("Switch")
equippedWeaponRegister.weaponType = "Auto"
end
UpdateWepGUI()
end
elseif Key == Enum.KeyCode["F"] and not Reloading and not isFiring then
if Equipped then
if not isHolstering then
runWeapon(true)
else
runWeapon(false)
end
end
end
end
function userInput(inputObject, GPE)
if GPE then return end
if inputObject.UserInputType == Enum.UserInputType.Keyboard then
keyDown(inputObject.KeyCode)
end
end
function newchar(newCharacter)
loadedAnimations = {}
registeredWeapons = {}
local Backpack = LocalPlayer:WaitForChild("Backpack")
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
Character = newCharacter
Humanoid = newCharacter:WaitForChild("Humanoid")
weaponGUI = PlayerGui:WaitForChild("GunGUI")
Backpack.ChildAdded:Connect(function(child)
if child:IsA("Tool") then
register(child)
end
end)
Backpack.ChildRemoved:Connect(function(Child)
if Child:IsA("Tool") then
if Child.Parent == Character then return end
if registeredWeapons[Child] then
registeredWeapons[Child]["equippedConnection"]:Disconnect()
registeredWeapons[Child]["unequippedConnection"]:Disconnect()
registeredWeapons[Child] = nil
end
end
end)
Humanoid.Died:Connect(function()
Humanoid:UnequipTools()
for i,v in pairs(Backpack:GetChildren()) do
if registeredWeapons[v] then
registeredWeapons[v]["equippedConnection"]:Disconnect()
registeredWeapons[v]["unequippedConnection"]:Disconnect()
end
end
registeredWeapons = {}
equippedWeapon = nil
equippedWeaponInfo = {}
equippedWeaponRegister = {}
Equipped = false
end)
for i,v in pairs(Backpack:GetChildren()) do
if v:IsA("Tool") then
if not registeredWeapons[v] then
register(v)
end
end
end
end
function hRender(Target, Origin, Color)
createRay(Target, {Origin = Origin, Color = Color})
end
RenderReq.OnClientEvent:Connect(hRender)
LocalPlayer.CharacterAdded:Connect(newchar)
UserInputS.InputBegan:Connect(userInput)
repeat wait() until LocalPlayer.Character
if not processedFirstChar then
newchar(LocalPlayer.Character)
end
game:GetService("RunService"):BindToRenderStep("GunClientThread", Enum.RenderPriority.Camera.Value, function(delta)
for i, v in next, GunClient.bullets, nil do
local new = 850 * v.s
if v.s >= 0 then
if v.ready then
v.t.Enabled = false
game.Debris:AddItem(v.a1, v.t.Lifetime + 1)
game.Debris:AddItem(v.a2, v.t.Lifetime + 1)
table.insert(GunClient.returnPool, {
tick = tick(),
lifetime = v.t.Lifetime + 1,
pool = pool,
obj = v.t
})
table.remove(GunClient.bullets, i)
else
local fd = math.min(new, v.l)
v.a1.Position = v.dir * Vector3.new(0.1, 0, -fd)
v.a2.Position = v.dir * Vector3.new(-0.1, 0, -fd)
v.ready = fd >= v.l
end
end
v.s = v.s + delta
end
for i, v in next, GunClient.timeout, nil do
if tick() - v.tick > v.lifetime then
v.func()
table.remove(GunClient.timeout, i)
end
end
for i, v in next, GunClient.returnPool, nil do
if tick() - v.tick > v.lifetime then
v.pool:ReturnInstance(v.obj)
table.remove(GunClient.returnPool, i)
end
end
end)
Thanks!