Add FPS game example, auto-connect plugin, and Python injection tools
- 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>
This commit is contained in:
383
examples/fps-game/part2_weapons.lua
Normal file
383
examples/fps-game/part2_weapons.lua
Normal file
@@ -0,0 +1,383 @@
|
||||
-- ═══════════════════════════════════════════════════════════════════
|
||||
-- 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.")
|
||||
Reference in New Issue
Block a user