- Updated RobloxMCPPlugin with HTTP polling (auto-enables HttpService) - Added 20-weapon FPS game example (CoD-style) - Added Python studio-inject.py for command bar injection via Win32 API - Added auto-connect setup scripts (VBS + PowerShell) - Updated MCP server with all FPS game tools Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
372 lines
12 KiB
Lua
372 lines
12 KiB
Lua
-- ═══════════════════════════════════════════════════════════════════
|
|
-- MINI CALL OF DUTY - FPS Game Setup (Part 5: Weapon Client Script)
|
|
-- ═══════════════════════════════════════════════════════════════════
|
|
|
|
local SG = game:GetService("StarterGui")
|
|
|
|
-- Get the HUD that was already created in Part 4
|
|
local hudGui = SG:FindFirstChild("FPS_HUD")
|
|
|
|
-- Add the weapon controller LocalScript
|
|
local weaponScript = Instance.new("LocalScript")
|
|
weaponScript.Name = "WeaponController"
|
|
weaponScript.Parent = hudGui
|
|
weaponScript.Source = [[
|
|
local Players = game:GetService("Players")
|
|
local RS = game:GetService("ReplicatedStorage")
|
|
local RunService = game:GetService("RunService")
|
|
local UIS = game:GetService("UserInputService")
|
|
local Events = RS:WaitForChild("Events")
|
|
local Shared = RS:WaitForChild("Shared")
|
|
local WeaponData = require(Shared:WaitForChild("WeaponData"))
|
|
|
|
local player = Players.LocalPlayer
|
|
local camera = workspace.CurrentCamera
|
|
local mouse = player:GetMouse()
|
|
|
|
-- Weapon state
|
|
local currentWeapon = "M4A1"
|
|
local weapon = WeaponData[currentWeapon]
|
|
local ammo = weapon.magSize
|
|
local reserveAmmo = weapon.maxAmmo
|
|
local isReloading = false
|
|
local lastShot = 0
|
|
local isADS = false
|
|
local isSprinting = false
|
|
local isFiring = false
|
|
local recoilX = 0
|
|
local recoilY = 0
|
|
|
|
-- UI references
|
|
local scriptParent = script.Parent
|
|
local crosshair = scriptParent:WaitForChild("Crosshair")
|
|
local hitMarker = scriptParent:WaitForChild("HitMarker")
|
|
local weaponPanel = scriptParent:WaitForChild("WeaponPanel")
|
|
local ammoLabel = weaponPanel:WaitForChild("AmmoLabel")
|
|
local weaponLabel = weaponPanel:WaitForChild("WeaponName")
|
|
local reserveLabel = weaponPanel:WaitForChild("ReserveLabel")
|
|
local reloadBar = scriptParent:WaitForChild("ReloadBar")
|
|
local reloadFill = reloadBar:WaitForChild("Fill")
|
|
local scoreLabel = scriptParent:WaitForChild("ScoreFrame"):WaitForChild("ScoreLabel")
|
|
local damageVignette = scriptParent:WaitForChild("DamageVignette")
|
|
|
|
local kills = 0
|
|
local deaths = 0
|
|
|
|
local function updateHUD()
|
|
if ammoLabel then
|
|
ammoLabel.Text = ammo .. " / " .. reserveAmmo
|
|
if ammo <= math.floor(weapon.magSize * 0.25) then
|
|
ammoLabel.TextColor3 = Color3.fromRGB(255, 80, 80)
|
|
else
|
|
ammoLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
|
|
end
|
|
end
|
|
if weaponLabel then
|
|
weaponLabel.Text = weapon.displayName:upper()
|
|
end
|
|
if reserveLabel then
|
|
reserveLabel.Text = isReloading and "RELOADING..." or ""
|
|
end
|
|
if scoreLabel then
|
|
scoreLabel.Text = "KILLS: " .. kills .. " | DEATHS: " .. deaths
|
|
end
|
|
end
|
|
|
|
local function shoot()
|
|
if isReloading then return end
|
|
if ammo <= 0 then
|
|
-- Auto reload
|
|
if reserveAmmo > 0 then
|
|
-- Play empty click sound via visual feedback
|
|
end
|
|
return
|
|
end
|
|
if tick() - lastShot < weapon.fireRate then return end
|
|
|
|
lastShot = tick()
|
|
ammo = ammo - 1
|
|
|
|
-- Recoil
|
|
recoilX = recoilX + (math.random() - 0.5) * weapon.recoil.x
|
|
recoilY = weapon.recoil.y * 0.3
|
|
camera.CFrame = camera.CFrame * CFrame.Angles(
|
|
math.rad(-recoilY),
|
|
math.rad(recoilX * 0.1),
|
|
0
|
|
)
|
|
|
|
-- Spread
|
|
local spreadAmount = isADS and weapon.spread.ads or weapon.spread.hip
|
|
local spread = Vector3.new(
|
|
(math.random() - 0.5) * spreadAmount * 0.01,
|
|
(math.random() - 0.5) * spreadAmount * 0.01,
|
|
0
|
|
)
|
|
|
|
-- Raycast
|
|
local rayDirection = (camera.CFrame.LookVector + spread) * weapon.range
|
|
local raycastParams = RaycastParams.new()
|
|
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
|
|
local char = player.Character
|
|
if char then raycastParams.FilterDescendantsInstances = {char} end
|
|
|
|
local result = workspace:Raycast(camera.CFrame.Position, rayDirection, raycastParams)
|
|
|
|
-- Muzzle flash
|
|
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.04)
|
|
|
|
if result then
|
|
-- Bullet trail
|
|
local trail = Instance.new("Part")
|
|
local trailLen = (camera.CFrame.Position - result.Position).Magnitude
|
|
trail.Size = Vector3.new(0.08, 0.08, trailLen)
|
|
trail.CFrame = CFrame.new(camera.CFrame.Position, result.Position)
|
|
* CFrame.new(0, 0, -trailLen / 2)
|
|
trail.Anchored = true
|
|
trail.CanCollide = false
|
|
trail.Color = Color3.fromRGB(255, 220, 100)
|
|
trail.Material = Enum.Material.Neon
|
|
trail.Transparency = 0.4
|
|
trail.Parent = workspace
|
|
game:GetService("Debris"):AddItem(trail, 0.06)
|
|
|
|
-- Impact spark
|
|
local spark = Instance.new("Part")
|
|
spark.Size = Vector3.new(0.4, 0.4, 0.4)
|
|
spark.Shape = Enum.PartType.Ball
|
|
spark.Color = Color3.fromRGB(255, 180, 50)
|
|
spark.Material = Enum.Material.Neon
|
|
spark.Anchored = true
|
|
spark.CanCollide = false
|
|
spark.Position = result.Position
|
|
spark.Parent = workspace
|
|
game:GetService("Debris"):AddItem(spark, 0.12)
|
|
|
|
-- Smoke puff at impact
|
|
local smoke = Instance.new("Part")
|
|
smoke.Size = Vector3.new(1, 1, 1)
|
|
smoke.Shape = Enum.PartType.Ball
|
|
smoke.Color = Color3.fromRGB(120, 120, 110)
|
|
smoke.Transparency = 0.5
|
|
smoke.Anchored = true
|
|
smoke.CanCollide = false
|
|
smoke.Position = result.Position
|
|
smoke.Parent = workspace
|
|
game:GetService("Debris"):AddItem(smoke, 0.3)
|
|
|
|
-- Send to server
|
|
Events:WaitForChild("ShootEvent"):FireServer({
|
|
origin = camera.CFrame.Position,
|
|
direction = rayDirection,
|
|
hit = result.Instance,
|
|
hitPos = result.Position,
|
|
normal = result.Normal,
|
|
weapon = currentWeapon,
|
|
})
|
|
else
|
|
-- Shot into air - just trail to max range
|
|
local endPoint = camera.CFrame.Position + rayDirection
|
|
local trail = Instance.new("Part")
|
|
local trailLen = weapon.range
|
|
trail.Size = Vector3.new(0.06, 0.06, trailLen)
|
|
trail.CFrame = CFrame.new(camera.CFrame.Position, endPoint)
|
|
* CFrame.new(0, 0, -trailLen / 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.04)
|
|
end
|
|
|
|
-- Auto reload when empty
|
|
if ammo <= 0 and reserveAmmo > 0 then
|
|
task.delay(0.3, function() reload() end)
|
|
end
|
|
|
|
updateHUD()
|
|
end
|
|
|
|
local function reload()
|
|
if isReloading then return end
|
|
if ammo >= weapon.magSize then return end
|
|
if reserveAmmo <= 0 then return end
|
|
|
|
isReloading = true
|
|
reloadBar.Visible = true
|
|
|
|
local startTime = tick()
|
|
local conn
|
|
conn = RunService.RenderStepped:Connect(function()
|
|
local elapsed = tick() - startTime
|
|
local pct = math.clamp(elapsed / weapon.reloadTime, 0, 1)
|
|
reloadFill.Size = UDim2.new(pct * 200, 0, 1, 0)
|
|
|
|
if pct >= 1 then
|
|
conn:Disconnect()
|
|
local needed = weapon.magSize - ammo
|
|
local toLoad = math.min(needed, reserveAmmo)
|
|
ammo = ammo + toLoad
|
|
reserveAmmo = reserveAmmo - toLoad
|
|
isReloading = false
|
|
reloadBar.Visible = false
|
|
reloadFill.Size = UDim2.new(0, 0, 1, 0)
|
|
updateHUD()
|
|
end
|
|
end)
|
|
|
|
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 not weapon.automatic then shoot() end
|
|
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 c = player.Character
|
|
if c then
|
|
local h = c:FindFirstChildOfClass("Humanoid")
|
|
if h then h.WalkSpeed = 8 end
|
|
end
|
|
end
|
|
|
|
if input.KeyCode == Enum.KeyCode.R then reload() end
|
|
|
|
-- Weapon switch
|
|
if input.KeyCode == Enum.KeyCode.One then currentWeapon = "M4A1"
|
|
elseif input.KeyCode == Enum.KeyCode.Two then currentWeapon = "AK47"
|
|
elseif input.KeyCode == Enum.KeyCode.Three then currentWeapon = "Sniper"
|
|
elseif input.KeyCode == Enum.KeyCode.Four then currentWeapon = "Shotgun" end
|
|
|
|
if input.KeyCode >= Enum.KeyCode.One and input.KeyCode <= Enum.KeyCode.Four then
|
|
weapon = WeaponData[currentWeapon]
|
|
ammo = weapon.magSize
|
|
reserveAmmo = weapon.maxAmmo
|
|
isReloading = false
|
|
reloadBar.Visible = 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 c = player.Character
|
|
if c then
|
|
local h = c:FindFirstChildOfClass("Humanoid")
|
|
if h then h.WalkSpeed = 20 end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Track kills/deaths from events
|
|
Events:WaitForChild("KillEvent").OnClientEvent:Connect(function(data)
|
|
if data.killer == player.Name then
|
|
kills = kills + 1
|
|
end
|
|
if data.victim == player.Name then
|
|
deaths = deaths + 1
|
|
end
|
|
updateHUD()
|
|
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 c = player.Character
|
|
if c then
|
|
local h = c:FindFirstChildOfClass("Humanoid")
|
|
if h and h.MoveDirection.Magnitude > 0 then
|
|
if isSprinting then
|
|
h.WalkSpeed = 30
|
|
elseif not UIS:IsKeyDown(Enum.KeyCode.LeftControl) then
|
|
h.WalkSpeed = 20
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Auto-fire for automatic weapons
|
|
if isFiring and weapon.automatic then shoot() end
|
|
|
|
-- Recoil recovery
|
|
recoilX = recoilX * 0.9
|
|
recoilY = recoilY * 0.85
|
|
|
|
-- Crosshair spread
|
|
local spreadPx = isADS and 2 or (isSprinting and 15 or 6)
|
|
if isFiring then spreadPx = spreadPx + 4 end
|
|
for _, child in ipairs(crosshair:GetChildren()) do
|
|
if child.Name == "Top" then child.Position = UDim2.new(0, 9, 0, 9 - spreadPx)
|
|
elseif child.Name == "Bottom" then child.Position = UDim2.new(0, 9, 0, 9 + spreadPx)
|
|
elseif child.Name == "Left" then child.Position = UDim2.new(0, 9 - spreadPx, 0, 9)
|
|
elseif child.Name == "Right" then child.Position = UDim2.new(0, 9 + spreadPx, 0, 9)
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- First person lock
|
|
UIS.MouseIconEnabled = false
|
|
player.CameraMode = Enum.CameraMode.LockFirstPerson
|
|
|
|
player.CharacterAdded:Connect(function()
|
|
ammo = weapon.magSize
|
|
reserveAmmo = weapon.maxAmmo
|
|
kills = 0
|
|
deaths = 0
|
|
isReloading = false
|
|
reloadBar.Visible = false
|
|
updateHUD()
|
|
end)
|
|
|
|
updateHUD()
|
|
print("═══════════════════════════════════════════")
|
|
print(" MINI CALL OF DUTY - LOADED!")
|
|
print(" Controls:")
|
|
print(" WASD = Move")
|
|
print(" LMB = Shoot")
|
|
print(" RMB = Aim Down Sights")
|
|
print(" Shift = Sprint")
|
|
print(" Ctrl = Crouch")
|
|
print(" R = Reload")
|
|
print(" 1-4 = Switch Weapon")
|
|
print(" Weapons: M4A1(1), AK-47(2), AWP Sniper(3), SPAS-12(4)")
|
|
print("═══════════════════════════════════════════")
|
|
]]
|
|
|
|
print("[CoD FPS] Part 5/5 complete: Weapon controller script created.")
|
|
print("═══════════════════════════════════════════")
|
|
print(" ALL PARTS COMPLETE! Press PLAY in Studio to start.")
|
|
print("═══════════════════════════════════════════")
|