-- ═══════════════════════════════════════════════════════════════════ -- MINI CALL OF DUTY - FPS Game Setup (Part 2: Weapon System) -- ═══════════════════════════════════════════════════════════════════ local RS = game:GetService("ReplicatedStorage") -- ═══════════════════════════════════════════════════════════════ -- WEAPON DATA MODULE -- ═══════════════════════════════════════════════════════════════ local weaponData = Instance.new("ModuleScript") weaponData.Name = "WeaponData" weaponData.Parent = RS:FindFirstChild("Shared") weaponData.Source = [[ local Weapons = { M4A1 = { name = "M4A1", displayName = "M4A1 Carbine", damage = 25, fireRate = 0.09, -- seconds between shots reloadTime = 2.2, magSize = 30, maxAmmo = 210, range = 300, headshotMult = 2.5, recoil = {x = 0.8, y = 1.2}, spread = {hip = 3, ads = 0.5}, aimSpeed = 0.15, moveSpeedMult = 0.95, automatic = true, adsFOV = 50, }, AK47 = { name = "AK-47", displayName = "AK-47", damage = 33, fireRate = 0.1, reloadTime = 2.5, magSize = 30, maxAmmo = 210, range = 280, headshotMult = 2.0, recoil = {x = 1.2, y = 1.8}, spread = {hip = 4, ads = 0.8}, aimSpeed = 0.18, moveSpeedMult = 0.92, automatic = true, adsFOV = 48, }, Sniper = { name = "AWP", displayName = "AWP Sniper", damage = 95, fireRate = 1.2, reloadTime = 3.5, magSize = 5, maxAmmo = 30, range = 800, headshotMult = 3.0, recoil = {x = 3, y = 5}, spread = {hip = 8, ads = 0.1}, aimSpeed = 0.25, moveSpeedMult = 0.85, automatic = false, adsFOV = 20, }, Shotgun = { name = "SPAS-12", displayName = "SPAS-12 Shotgun", damage = 15, -- per pellet (8 pellets) fireRate = 0.7, reloadTime = 3.0, magSize = 8, maxAmmo = 40, range = 50, headshotMult = 1.5, recoil = {x = 4, y = 6}, spread = {hip = 12, ads = 8}, aimSpeed = 0.15, moveSpeedMult = 0.88, automatic = false, pellets = 8, adsFOV = 55, }, } return Weapons ]] -- ═══════════════════════════════════════════════════════════════ -- CLIENT WEAPON CONTROLLER (LocalScript) -- ═══════════════════════════════════════════════════════════════ local weaponClient = Instance.new("LocalScript") weaponClient.Name = "WeaponClient" weaponClient.Parent = game:GetService("StarterPlayer"):FindFirstChild("StarterPlayerScripts") weaponClient.Source = [[ local Players = game:GetService("Players") local RS = game:GetService("ReplicatedStorage") local UIS = game:GetService("UserInputService") local RunService = game:GetService("RunService") local Events = RS:WaitForChild("Events") local player = Players.LocalPlayer local camera = workspace.CurrentCamera local Weapons = require(RS:WaitForChild("Shared"):WaitForChild("WeaponData")) -- State local currentWeapon = "M4A1" local weapon = Weapons[currentWeapon] local ammo = weapon.magSize local reserveAmmo = weapon.maxAmmo local isReloading = false local isADS = false local isSprinting = false local isFiring = false local lastFireTime = 0 local canShoot = true -- Recoil tracking local recoilX = 0 local recoilY = 0 local recoilRecoverySpeed = 8 -- Functions local function updateHUD() local hud = player.PlayerGui:FindFirstChild("FPS_HUD") if not hud then return end local frame = hud:FindFirstChild("MainFrame") if not frame then return end local ammoText = frame:FindFirstChild("AmmoDisplay") if ammoText then ammoText.Text = ammo .. " / " .. reserveAmmo end local weaponText = frame:FindFirstChild("WeaponName") if weaponText then weaponText.Text = weapon.displayName end local healthBar = frame:FindFirstChild("HealthBar") local healthFill = frame:FindFirstChild("HealthFill") if healthBar and healthFill then local char = player.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if hum then local pct = hum.Health / hum.MaxHealth healthFill.Size = UDim2.new(pct * 0.18, 0, 0.025, 0) if pct < 0.3 then healthFill.BackgroundColor3 = Color3.fromRGB(200, 30, 30) elseif pct < 0.6 then healthFill.BackgroundColor3 = Color3.fromRGB(200, 180, 30) else healthFill.BackgroundColor3 = Color3.fromRGB(30, 200, 30) end end end local scoreText = frame:FindFirstChild("ScoreDisplay") if scoreText then scoreText.Text = "KILLS: " .. tostring(player:GetAttribute("Kills") or 0) end end local function shoot() if isReloading or ammo <= 0 or not canShoot then return end if tick() - lastFireTime < weapon.fireRate then return end lastFireTime = tick() ammo = ammo - 1 canShoot = false -- Fire raycast local mousePos = UIS:GetMouseLocation() local ray = camera:ViewportPointToRay(mousePos.X, mousePos.Y) local spreadMult = isADS and weapon.spread.ads or weapon.spread.hip local spread = CFrame.new( math.random(-100, 100) / 100 * spreadMult, math.random(-100, 100) / 100 * spreadMult, math.random(-100, 100) / 100 * spreadMult ) * 0.01 local direction = (ray.Direction.Unit + spread.Position).Unit local pellets = weapon.pellets or 1 for _ = 1, pellets do local hitRay = RaycastParams.new() hitRay.FilterDescendantsInstances = {player.Character or {}} hitRay.FilterType = Enum.RaycastFilterType.Exclude local result = workspace:Raycast(ray.Origin, direction * weapon.range, hitRay) if result then Events:FindFirstChild("ShootEvent"):FireServer({ origin = ray.Origin, direction = direction * weapon.range, hit = result.Instance, hitPos = result.Position, normal = result.Normal, weapon = currentWeapon, }) -- Muzzle flash visual local flash = Instance.new("Part") flash.Size = Vector3.new(0.3, 0.3, 0.3) flash.Shape = Enum.PartType.Ball flash.Color = Color3.fromRGB(255, 200, 50) flash.Material = Enum.Material.Neon flash.Anchored = true flash.CanCollide = false flash.Position = camera.CFrame.Position + camera.CFrame.LookVector * 3 flash.Parent = workspace game:GetService("Debris"):AddItem(flash, 0.05) -- Bullet trail local trail = Instance.new("Part") trail.Size = Vector3.new(0.1, 0.1, weapon.range) trail.CFrame = CFrame.new(camera.CFrame.Position, result.Position) * CFrame.new(0, 0, -weapon.range/2) trail.Anchored = true trail.CanCollide = false trail.Color = Color3.fromRGB(255, 220, 100) trail.Material = Enum.Material.Neon trail.Transparency = 0.5 trail.Parent = workspace game:GetService("Debris"):AddItem(trail, 0.03) -- Impact effect local impact = Instance.new("Part") impact.Size = Vector3.new(0.5, 0.5, 0.5) impact.Shape = Enum.PartType.Ball impact.Color = Color3.fromRGB(255, 150, 50) impact.Material = Enum.Material.Neon impact.Anchored = true impact.CanCollide = false impact.Position = result.Position impact.Parent = workspace game:GetService("Debris"):AddItem(impact, 0.1) end end -- Apply recoil if isADS then recoilX = recoilX - weapon.recoil.x * 0.4 recoilY = recoilY + weapon.recoil.y * 0.4 else recoilX = recoilX - weapon.recoil.x recoilY = recoilY + weapon.recoil.y end -- Screen shake local shake = isADS and 0.002 or 0.005 camera.CFrame = camera.CFrame * CFrame.new( math.random(-100,100)/100 * shake, math.random(-100,100)/100 * shake, 0 ) task.wait(weapon.fireRate) canShoot = true updateHUD() if ammo <= 0 then reload() end end function reload() if isReloading or reserveAmmo <= 0 then return end isReloading = true Events:FindFirstChild("ReloadEvent"):FireServer() task.wait(weapon.reloadTime) local needed = weapon.magSize - ammo local available = math.min(needed, reserveAmmo) ammo = ammo + available reserveAmmo = reserveAmmo - available isReloading = false updateHUD() end -- Input handling UIS.InputBegan:Connect(function(input, processed) if processed then return end if input.UserInputType == Enum.UserInputType.MouseButton2 then isADS = true if isSprinting then isSprinting = false end end if input.UserInputType == Enum.UserInputType.MouseButton1 then isFiring = true if isSprinting then isSprinting = false end end if input.KeyCode == Enum.KeyCode.LeftShift then if not isADS then isSprinting = true end end if input.KeyCode == Enum.KeyCode.LeftControl then local char = player.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum then hum.WalkSpeed = 8 end end end if input.KeyCode == Enum.KeyCode.R then reload() end -- Weapon switch: 1-4 if input.KeyCode == Enum.KeyCode.One then currentWeapon = "M4A1" end if input.KeyCode == Enum.KeyCode.Two then currentWeapon = "AK47" end if input.KeyCode == Enum.KeyCode.Three then currentWeapon = "Sniper" end if input.KeyCode == Enum.KeyCode.Four then currentWeapon = "Shotgun" end if input.KeyCode >= Enum.KeyCode.One and input.KeyCode <= Enum.KeyCode.Four then weapon = Weapons[currentWeapon] ammo = weapon.magSize reserveAmmo = weapon.maxAmmo isReloading = false updateHUD() end end) UIS.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton2 then isADS = false end if input.UserInputType == Enum.UserInputType.MouseButton1 then isFiring = false end if input.KeyCode == Enum.KeyCode.LeftShift then isSprinting = false end if input.KeyCode == Enum.KeyCode.LeftControl then local char = player.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum then hum.WalkSpeed = 20 end end end end) -- Main loop RunService.RenderStepped:Connect(function() -- Camera FOV for ADS local targetFOV = isADS and weapon.adsFOV or 70 camera.FieldOfView = camera.FieldOfView + (targetFOV - camera.FieldOfView) * 0.2 -- Sprint speed local char = player.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum and hum.MoveDirection.Magnitude > 0 then if isSprinting then hum.WalkSpeed = 30 elseif not UIS:IsKeyDown(Enum.KeyCode.LeftControl) then hum.WalkSpeed = 20 end end end -- Auto-fire if isFiring and weapon.automatic then shoot() end -- Recoil recovery recoilX = recoilX + (0 - recoilX) * math.clamp(recoilRecoverySpeed * 0.01, 0, 1) recoilY = recoilY + (0 - recoilY) * math.clamp(recoilRecoverySpeed * 0.01, 0, 1) updateHUD() end) -- Lock mouse for FPS UIS.MouseIconEnabled = false player.CharacterAdded:Connect(function() ammo = weapon.magSize reserveAmmo = weapon.maxAmmo updateHUD() end) updateHUD() print("[WeaponClient] Loaded - Controls: LMB=Shoot, RMB=ADS, Shift=Sprint, Ctrl=Crouch, R=Reload, 1-4=Switch weapon") ]] print("[CoD FPS] Part 2/5 complete: Weapon system created.")