- inject_gta_city.py: Full GTA-style city with 20 buildings, roads, cars, street lights, traffic lights, trees, 15 human enemies with varied skin/clothing, and 10 COD weapons with visible gun models - inject_bg.py: Background injection using SendMessage/PostMessage Win32 API - inject_bg2.py: PostMessage approach targeting main window for WPF apps - inject_cod_final.py: Working COD game injection (7-step sequential) - cod_inject.py: Combined COD game builder with proper Studio launch - roblox-fps-p1-p6: Split Lua scripts for multi-part injection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1315 lines
51 KiB
Lua
1315 lines
51 KiB
Lua
-- ═══════════════════════════════════════════════════════════════
|
|
-- MINI CALL OF DUTY - COMPLETE GAME (Single Injection)
|
|
-- 20 Weapons | Enemy AI | Full HUD | Urban Map
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
-- ── CLEAN SLATE ──
|
|
for _, v in pairs({"GameServer", "FPS_HUD", "WeaponClient", "WeaponController", "PlayerSetup"}) do
|
|
local s = game.ServerScriptService:FindFirstChild(v)
|
|
if s then s:Destroy() end
|
|
local g = game.StarterGui:FindFirstChild(v)
|
|
if g then g:Destroy() end
|
|
local p = game.StarterPlayer.StarterPlayerScripts:FindFirstChild(v)
|
|
if p then p:Destroy() end
|
|
end
|
|
local shared = game.ReplicatedStorage:FindFirstChild("Shared")
|
|
if shared then shared:Destroy() end
|
|
local events = game.ReplicatedStorage:FindFirstChild("Events")
|
|
if events then events:Destroy() end
|
|
|
|
-- Clean workspace
|
|
for _, v in pairs(workspace:GetChildren()) do
|
|
if v:IsA("Model") or v:IsA("Part") or v:IsA("Folder") then
|
|
if v.Name ~= "Camera" and v.Name ~= "Terrain" then
|
|
v:Destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
wait(0.5)
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 1: MAP
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
-- Ground
|
|
local ground = Instance.new("Part", workspace)
|
|
ground.Name = "Ground"
|
|
ground.Size = Vector3.new(400, 2, 400)
|
|
ground.Position = Vector3.new(0, -1, 0)
|
|
ground.Anchored = true
|
|
ground.BrickColor = BrickColor.new("Dark stone grey")
|
|
ground.Material = Enum.Material.Concrete
|
|
|
|
-- Boundary walls
|
|
local wallData = {
|
|
{n="NorthWall",s={400,20,2},p={0,10,-200}},
|
|
{n="SouthWall",s={400,20,2},p={0,10,200}},
|
|
{n="EastWall",s={2,20,400},p={200,10,0}},
|
|
{n="WestWall",s={2,20,400},p={-200,10,0}},
|
|
}
|
|
for _,w in ipairs(wallData) do
|
|
local wall = Instance.new("Part", workspace)
|
|
wall.Name = w.n
|
|
wall.Size = Vector3.new(unpack(w.s))
|
|
wall.Position = Vector3.new(unpack(w.p))
|
|
wall.Anchored = true
|
|
wall.BrickColor = BrickColor.new("Dark stone grey")
|
|
wall.Material = Enum.Material.Concrete
|
|
wall.Transparency = 0.3
|
|
end
|
|
|
|
-- Buildings
|
|
local buildings = {
|
|
{n="Building_Alpha",s={30,20,25},p={-60,10,-80},c="Medium stone grey"},
|
|
{n="Building_Bravo",s={25,18,30},p={70,9,-60},c="Brown"},
|
|
{n="Building_Charlie",s={35,22,20},p={-40,11,70},c="Dark stone grey"},
|
|
{n="Building_Delta",s={20,16,35},p={80,8,80},c="Medium stone grey"},
|
|
{n="Building_Echo",s={28,24,28},p={0,12,-150},c="Brown"},
|
|
}
|
|
for _,b in ipairs(buildings) do
|
|
local bld = Instance.new("Model", workspace)
|
|
bld.Name = b.n
|
|
-- Main structure
|
|
local main = Instance.new("Part", bld)
|
|
main.Name = "Main"
|
|
main.Size = Vector3.new(unpack(b.s))
|
|
main.Position = Vector3.new(unpack(b.p))
|
|
main.Anchored = true
|
|
main.BrickColor = BrickColor.new(b.c)
|
|
main.Material = Enum.Material.Brick
|
|
end
|
|
|
|
-- Cover objects (sandbags, crates, barrels)
|
|
local coverPositions = {
|
|
{-20,1,0},{20,1,0},{0,1,-30},{0,1,30},
|
|
{-50,1,20},{50,1,-20},{-30,1,-50},{30,1,50},
|
|
{-80,1,0},{80,1,0},{0,1,-80},{0,1,80},
|
|
{-10,1,-120},{10,1,120},{-100,1,-40},{100,1,40},
|
|
{-60,1,40},{60,1,-40},{-40,1,-120},{40,1,120},
|
|
{110,1,0},{-110,1,0},{0,1,110},{0,1,-110},
|
|
}
|
|
for i,cp in ipairs(coverPositions) do
|
|
if i % 3 == 0 then
|
|
-- Barrel
|
|
local barrel = Instance.new("Part", workspace)
|
|
barrel.Name = "Barrel_"..i
|
|
barrel.Size = Vector3.new(3, 4, 3)
|
|
barrel.Position = Vector3.new(cp[1], cp[2]+2, cp[3])
|
|
barrel.Anchored = true
|
|
barrel.BrickColor = BrickColor.new("Reddish brown")
|
|
barrel.Material = Enum.Material.Metal
|
|
barrel.Shape = Enum.PartType.Cylinder
|
|
barrel.Orientation = Vector3.new(0, 0, 90)
|
|
elseif i % 3 == 1 then
|
|
-- Crate
|
|
local crate = Instance.new("Part", workspace)
|
|
crate.Name = "Crate_"..i
|
|
crate.Size = Vector3.new(5, 5, 5)
|
|
crate.Position = Vector3.new(cp[1], cp[2]+2.5, cp[3])
|
|
crate.Anchored = true
|
|
crate.BrickColor = BrickColor.new("Brown")
|
|
crate.Material = Enum.Material.Wood
|
|
else
|
|
-- Sandbag
|
|
local sandbag = Instance.new("Part", workspace)
|
|
sandbag.Name = "Sandbag_"..i
|
|
sandbag.Size = Vector3.new(8, 3, 3)
|
|
sandbag.Position = Vector3.new(cp[1], cp[2]+1.5, cp[3])
|
|
sandbag.Anchored = true
|
|
sandbag.BrickColor = BrickColor.new("Brick yellow")
|
|
sandbag.Material = Enum.Material.Sand
|
|
end
|
|
end
|
|
|
|
-- Watchtowers
|
|
local towers = {{-120,0,-120},{120,0,-120},{-120,0,120},{120,0,120}}
|
|
for i,tp in ipairs(towers) do
|
|
local tower = Instance.new("Model", workspace)
|
|
tower.Name = "Watchtower_"..i
|
|
-- Base
|
|
local base = Instance.new("Part", tower)
|
|
base.Size = Vector3.new(6, 1, 6)
|
|
base.Position = Vector3.new(tp[1], 0.5, tp[3])
|
|
base.Anchored = true
|
|
base.BrickColor = BrickColor.new("Dark stone grey")
|
|
base.Material = Enum.Material.Metal
|
|
-- Legs
|
|
for _,off in ipairs({{-2,0,-2},{2,0,-2},{-2,0,2},{2,0,2}}) do
|
|
local leg = Instance.new("Part", tower)
|
|
leg.Size = Vector3.new(1, 15, 1)
|
|
leg.Position = Vector3.new(tp[1]+off[1], 7.5, tp[3]+off[2])
|
|
leg.Anchored = true
|
|
leg.BrickColor = BrickColor.new("Dark stone grey")
|
|
leg.Material = Enum.Material.Metal
|
|
end
|
|
-- Platform
|
|
local plat = Instance.new("Part", tower)
|
|
plat.Size = Vector3.new(8, 1, 8)
|
|
plat.Position = Vector3.new(tp[1], 15, tp[3])
|
|
plat.Anchored = true
|
|
plat.BrickColor = BrickColor.new("Brown")
|
|
plat.Material = Enum.Material.Wood
|
|
end
|
|
|
|
-- Spawn points
|
|
for _,sp in ipairs({{0,3,170},{-30,3,170},{30,3,170}}) do
|
|
local spPart = Instance.new("SpawnLocation", workspace)
|
|
spPart.Size = Vector3.new(6, 1, 6)
|
|
spPart.Position = Vector3.new(unpack(sp))
|
|
spPart.Anchored = true
|
|
spPart.CanCollide = false
|
|
spPart.Transparency = 1
|
|
spPart.Name = "SpawnPoint"
|
|
end
|
|
|
|
-- Lighting
|
|
local lighting = game:GetService("Lighting")
|
|
lighting.ClockTime = 17.5
|
|
lighting.Brightness = 0.5
|
|
lighting.FogEnd = 500
|
|
lighting.FogStart = 200
|
|
lighting.Ambient = Color3.fromRGB(100, 100, 120)
|
|
lighting.OutdoorAmbient = Color3.fromRGB(80, 80, 100)
|
|
|
|
local bloom = Instance.new("BloomEffect", lighting)
|
|
bloom.Intensity = 0.3
|
|
bloom.Size = 24
|
|
bloom.Threshold = 1
|
|
|
|
local cc = Instance.new("ColorCorrectionEffect", lighting)
|
|
cc.Contrast = 0.1
|
|
cc.Saturation = 0.1
|
|
cc.TintColor = Color3.fromRGB(255, 240, 220)
|
|
|
|
wait(0.3)
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 2: SHARED DATA (ReplicatedStorage)
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local shared = Instance.new("Folder", game.ReplicatedStorage)
|
|
shared.Name = "Shared"
|
|
|
|
-- RemoteEvents
|
|
local events = Instance.new("Folder", game.ReplicatedStorage)
|
|
events.Name = "Events"
|
|
|
|
Instance.new("RemoteEvent", events).Name = "HitEvent"
|
|
Instance.new("RemoteEvent", events).Name = "KillEvent"
|
|
Instance.new("RemoteEvent", events).Name = "DamageEvent"
|
|
Instance.new("RemoteEvent", events).Name = "WeaponSwitchEvent"
|
|
Instance.new("RemoteEvent", events).Name = "ReloadEvent"
|
|
Instance.new("RemoteEvent", events).Name = "PlayerDiedEvent"
|
|
Instance.new("RemoteEvent", events).Name = "EnemyKilledEvent"
|
|
Instance.new("RemoteEvent", events).Name = "HitMarkerEvent"
|
|
|
|
-- WeaponData ModuleScript with 20 WEAPONS
|
|
local wd = Instance.new("ModuleScript", shared)
|
|
wd.Name = "WeaponData"
|
|
|
|
wd.Source = [[
|
|
local WeaponData = {
|
|
-- ASSAULT RIFLES
|
|
{Name="M4A1 Carbine", Category="Assault Rifle", Key=Enum.KeyCode.One, Damage=25, HeadMulti=2.5, FireRate=0.1, MagSize=30, ReloadTime=2.2, Spread=0.02, ADS_Spread=0.008, Recoil=0.3, Range=300, Auto=true, AmmoType="5.56x45mm"},
|
|
{Name="AK-47", Category="Assault Rifle", Key=Enum.KeyCode.Two, Damage=28, HeadMulti=2.5, FireRate=0.12, MagSize=30, ReloadTime=2.5, Spread=0.03, ADS_Spread=0.012, Recoil=0.45, Range=280, Auto=true, AmmoType="7.62x39mm"},
|
|
{Name="FN SCAR-H", Category="Assault Rifle", Key=Enum.KeyCode.Three, Damage=33, HeadMulti=2.5, FireRate=0.11, MagSize=20, ReloadTime=2.6, Spread=0.025, ADS_Spread=0.01, Recoil=0.4, Range=320, Auto=true, AmmoType="7.62x51mm"},
|
|
{Name="M16A4", Category="Assault Rifle", Key=Enum.KeyCode.Four, Damage=30, HeadMulti=2.8, FireRate=0.075, MagSize=30, ReloadTime=2.3, Spread=0.015, ADS_Spread=0.005, Recoil=0.35, Range=350, Auto=false, BurstCount=3, AmmoType="5.56x45mm"},
|
|
{Name="FAMAS F1", Category="Assault Rifle", Key=Enum.KeyCode.Five, Damage=24, HeadMulti=2.5, FireRate=0.065, MagSize=25, ReloadTime=2.1, Spread=0.025, ADS_Spread=0.01, Recoil=0.35, Range=270, Auto=true, AmmoType="5.56x45mm"},
|
|
{Name="HK G36C", Category="Assault Rifle", Key=Enum.KeyCode.Six, Damage=26, HeadMulti=2.5, FireRate=0.09, MagSize=30, ReloadTime=2.2, Spread=0.02, ADS_Spread=0.008, Recoil=0.3, Range=300, Auto=true, AmmoType="5.56x45mm"},
|
|
|
|
-- SMGS
|
|
{Name="MP5A4", Category="SMG", Key=Enum.KeyCode.Seven, Damage=20, HeadMulti=2, FireRate=0.08, MagSize=30, ReloadTime=1.8, Spread=0.035, ADS_Spread=0.015, Recoil=0.15, Range=150, Auto=true, AmmoType="9x19mm"},
|
|
{Name="FN P90", Category="SMG", Key=Enum.KeyCode.Eight, Damage=21, HeadMulti=2, FireRate=0.065, MagSize=50, ReloadTime=2.5, Spread=0.04, ADS_Spread=0.018, Recoil=0.18, Range=140, Auto=true, AmmoType="5.7x28mm"},
|
|
{Name="HK MP7A2", Category="SMG", Key=Enum.KeyCode.Nine, Damage=19, HeadMulti=2, FireRate=0.07, MagSize=40, ReloadTime=2.0, Spread=0.03, ADS_Spread=0.012, Recoil=0.12, Range=130, Auto=true, AmmoType="4.6x30mm"},
|
|
{Name="UMP-45", Category="SMG", Key=Enum.KeyCode.Zero, Damage=23, HeadMulti=2, FireRate=0.11, MagSize=25, ReloadTime=2.0, Spread=0.04, ADS_Spread=0.018, Recoil=0.22, Range=160, Auto=true, AmmoType=".45 ACP"},
|
|
|
|
-- SNIPERS
|
|
{Name="AWP", Category="Sniper", Key=Enum.KeyCode.Q, Damage=95, HeadMulti=3, FireRate=1.4, MagSize=10, ReloadTime=3.5, Spread=0.001, ADS_Spread=0, Recoil=2.0, Range=800, Auto=false, ScopeZoom=8, AmmoType=".338 Lapua"},
|
|
{Name="Barrett M82", Category="Sniper", Key=Enum.KeyCode.E, Damage=150, HeadMulti=3, FireRate=1.8, MagSize=5, ReloadTime=4.0, Spread=0.002, ADS_Spread=0.001, Recoil=3.5, Range=1000, Auto=false, ScopeZoom=10, AmmoType=".50 BMG"},
|
|
{Name="SVD Dragunov", Category="Sniper", Key=Enum.KeyCode.T, Damage=55, HeadMulti=2.8, FireRate=0.5, MagSize=10, ReloadTime=3.0, Spread=0.005, ADS_Spread=0.002, Recoil=1.2, Range=600, Auto=false, ScopeZoom=4, AmmoType="7.62x54mm"},
|
|
|
|
-- SHOTGUNS
|
|
{Name="SPAS-12", Category="Shotgun", Key=Enum.KeyCode.Z, Damage=15, HeadMulti=1.5, FireRate=0.9, MagSize=8, ReloadTime=3.5, Spread=0.1, ADS_Spread=0.08, Recoil=1.5, Range=40, Auto=false, Pellets=8, AmmoType="12 Gauge"},
|
|
{Name="Remington 870", Category="Shotgun", Key=Enum.KeyCode.X, Damage=18, HeadMulti=1.5, FireRate=1.0, MagSize=6, ReloadTime=3.0, Spread=0.12, ADS_Spread=0.09, Recoil=1.8, Range=35, Auto=false, Pellets=6, AmmoType="12 Gauge"},
|
|
|
|
-- LMGS
|
|
{Name="M249 SAW", Category="LMG", Key=Enum.KeyCode.C, Damage=28, HeadMulti=2, FireRate=0.08, MagSize=100, ReloadTime=5.5, Spread=0.05, ADS_Spread=0.025, Recoil=0.5, Range=350, Auto=true, AmmoType="5.56x45mm"},
|
|
{Name="M134 Minigun", Category="LMG", Key=Enum.KeyCode.V, Damage=18, HeadMulti=1.8, FireRate=0.04, MagSize=200, ReloadTime=8.0, Spread=0.07, ADS_Spread=0.04, Recoil=0.35, Range=250, Auto=true, AmmoType="7.62x51mm"},
|
|
|
|
-- PISTOLS
|
|
{Name="Desert Eagle", Category="Pistol", Key=Enum.KeyCode.B, Damage=45, HeadMulti=3, FireRate=0.3, MagSize=7, ReloadTime=1.8, Spread=0.03, ADS_Spread=0.015, Recoil=0.8, Range=100, Auto=false, AmmoType=".50 AE"},
|
|
{Name="Glock 18C", Category="Pistol", Key=Enum.KeyCode.N, Damage=18, HeadMulti=2, FireRate=0.05, MagSize=20, ReloadTime=1.5, Spread=0.04, ADS_Spread=0.02, Recoil=0.15, Range=60, Auto=true, AmmoType="9x19mm"},
|
|
|
|
-- LAUNCHER
|
|
{Name="RPG-7", Category="Launcher", Key=Enum.KeyCode.G, Damage=200, HeadMulti=1, FireRate=2.0, MagSize=1, ReloadTime=4.5, Spread=0.01, ADS_Spread=0.005, Recoil=3.0, Range=200, Auto=false, Explosive=true, BlastRadius=20, AmmoType="PG-7VL"},
|
|
}
|
|
return WeaponData
|
|
]]
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 3: GAME SERVER (ServerScriptService)
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local gs = Instance.new("Script", game.ServerScriptService)
|
|
gs.Name = "GameServer"
|
|
gs.Source = [[
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
local Players = game:GetService("Players")
|
|
local Workspace = game:GetService("Workspace")
|
|
|
|
local Events = ReplicatedStorage:WaitForChild("Events")
|
|
local HitEvent = Events:WaitForChild("HitEvent")
|
|
local KillEvent = Events:WaitForChild("KillEvent")
|
|
local DamageEvent = Events:WaitForChild("DamageEvent")
|
|
local PlayerDiedEvent = Events:WaitForChild("PlayerDiedEvent")
|
|
local EnemyKilledEvent = Events:WaitForChild("EnemyKilledEvent")
|
|
local HitMarkerEvent = Events:WaitForChild("HitMarkerEvent")
|
|
|
|
-- Player management
|
|
Players.PlayerAdded:Connect(function(player)
|
|
player.CharacterAdded:Connect(function(character)
|
|
local humanoid = character:WaitForChild("Humanoid")
|
|
humanoid.MaxHealth = 100
|
|
humanoid.Health = 100
|
|
humanoid.WalkSpeed = 18
|
|
|
|
-- Spawn loadout
|
|
humanoid.Died:Connect(function()
|
|
PlayerDiedEvent:FireClient(player, player.Name)
|
|
task.delay(4, function()
|
|
if player and player.Character and player.Character:FindFirstChild("Humanoid") then
|
|
player.Character:FindFirstChild("Humanoid").Health = 0
|
|
end
|
|
player:LoadCharacter()
|
|
end)
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
-- Hit detection from clients
|
|
HitEvent.OnServerEvent:Connect(function(player, hitPart, damage, isHeadshot, weaponName)
|
|
if hitPart and hitPart.Parent and hitPart.Parent:FindFirstChild("Humanoid") then
|
|
local target = hitPart.Parent
|
|
local hum = target:FindFirstChild("Humanoid")
|
|
if hum and hum.Health > 0 then
|
|
local dmg = isHeadshot and damage * 2.5 or damage
|
|
hum:TakeDamage(dmg)
|
|
HitMarkerEvent:FireClient(player, isHeadshot)
|
|
|
|
-- Notify target they took damage
|
|
local targetPlayer = Players:GetPlayerFromCharacter(target)
|
|
if targetPlayer and targetPlayer ~= player then
|
|
DamageEvent:FireClient(targetPlayer, dmg)
|
|
end
|
|
|
|
if hum.Health <= 0 then
|
|
KillEvent:FireClient(player, "Enemy")
|
|
player.leaderstats.Kills.Value = player.leaderstats.Kills.Value + 1
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Enemy hit detection
|
|
EnemyKilledEvent.OnServerEvent:Connect(function(player, enemyName)
|
|
if player and player:FindFirstChild("leaderstats") then
|
|
player.leaderstats.Kills.Value = player.leaderstats.Kills.Value + 1
|
|
end
|
|
end)
|
|
|
|
-- Leaderstats
|
|
Players.PlayerAdded:Connect(function(player)
|
|
local ls = Instance.new("Folder", player)
|
|
ls.Name = "leaderstats"
|
|
local kills = Instance.new("IntValue", ls)
|
|
kills.Name = "Kills"
|
|
kills.Value = 0
|
|
local deaths = Instance.new("IntValue", ls)
|
|
deaths.Name = "Deaths"
|
|
deaths.Value = 0
|
|
|
|
player.CharacterAdded:Connect(function(char)
|
|
local hum = char:WaitForChild("Humanoid")
|
|
hum.Died:Connect(function()
|
|
deaths.Value = deaths.Value + 1
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
-- ═══════ ENEMY AI SYSTEM ═══════
|
|
local MAX_ENEMIES = 10
|
|
local ENEMY_SPawns = {
|
|
Vector3.new(-150, 3, -150), Vector3.new(150, 3, -150),
|
|
Vector3.new(-150, 3, 150), Vector3.new(150, 3, 150),
|
|
Vector3.new(0, 3, -150), Vector3.new(0, 3, 150),
|
|
Vector3.new(-150, 3, 0), Vector3.new(150, 3, 0),
|
|
Vector3.new(-100, 3, -100), Vector3.new(100, 3, 100),
|
|
}
|
|
|
|
local enemies = {}
|
|
local patrolPoints = {
|
|
Vector3.new(-60, 3, -80), Vector3.new(70, 3, -60),
|
|
Vector3.new(-40, 3, 70), Vector3.new(80, 3, 80),
|
|
Vector3.new(0, 3, -150), Vector3.new(-120, 3, -120),
|
|
Vector3.new(120, 3, -120), Vector3.new(-120, 3, 120),
|
|
Vector3.new(120, 3, 120), Vector3.new(0, 3, 0),
|
|
}
|
|
|
|
local function createEnemy(spawnPos)
|
|
local enemy = Instance.new("Model", Workspace)
|
|
enemy.Name = "EnemySoldier"
|
|
|
|
-- Body
|
|
local torso = Instance.new("Part", enemy)
|
|
torso.Name = "Torso"
|
|
torso.Size = Vector3.new(3, 3, 2)
|
|
torso.Position = spawnPos
|
|
torso.BrickColor = BrickColor.new("Dark green")
|
|
torso.Material = Enum.Material.Plastic
|
|
|
|
-- Head
|
|
local head = Instance.new("Part", enemy)
|
|
head.Name = "Head"
|
|
head.Size = Vector3.new(2, 2, 2)
|
|
head.Position = spawnPos + Vector3.new(0, 2.5, 0)
|
|
head.BrickColor = BrickColor.new("Medium stone grey")
|
|
head.Material = Enum.Material.Plastic
|
|
|
|
-- Legs
|
|
local ll = Instance.new("Part", enemy)
|
|
ll.Name = "Left Leg"
|
|
ll.Size = Vector3.new(1, 3, 1)
|
|
ll.Position = spawnPos + Vector3.new(-0.75, -2.5, 0)
|
|
ll.BrickColor = BrickColor.new("Dark green")
|
|
ll.Parent = enemy
|
|
|
|
local rl = Instance.new("Part", enemy)
|
|
rl.Name = "Right Leg"
|
|
rl.Size = Vector3.new(1, 3, 1)
|
|
rl.Position = spawnPos + Vector3.new(0.75, -2.5, 0)
|
|
rl.BrickColor = BrickColor.new("Dark green")
|
|
rl.Parent = enemy
|
|
|
|
-- Weld everything
|
|
local hum = Instance.new("Humanoid", enemy)
|
|
hum.MaxHealth = 100
|
|
hum.Health = 100
|
|
hum.WalkSpeed = 12
|
|
|
|
local rootPart = Instance.new("Part", enemy)
|
|
rootPart.Name = "HumanoidRootPart"
|
|
rootPart.Size = Vector3.new(2, 2, 1)
|
|
rootPart.Position = spawnPos
|
|
rootPart.Transparency = 1
|
|
rootPart.CanCollide = false
|
|
|
|
local function weld(part0, part1)
|
|
local w = Instance.new("Weld", part0)
|
|
w.Part0 = part0
|
|
w.Part1 = part1
|
|
end
|
|
weld(rootPart, torso)
|
|
weld(torso, head)
|
|
weld(torso, ll)
|
|
weld(torso, rl)
|
|
|
|
-- AI state
|
|
local state = {
|
|
model = enemy,
|
|
hum = hum,
|
|
status = "patrol",
|
|
targetPoint = patrolPoints[math.random(#patrolPoints)],
|
|
lastShot = 0,
|
|
detectRange = 60,
|
|
attackRange = 45,
|
|
}
|
|
|
|
-- Patrol behavior
|
|
local function patrolLoop()
|
|
while enemy.Parent and hum.Health > 0 do
|
|
task.wait(0.5)
|
|
if state.status == "patrol" then
|
|
local root = enemy:FindFirstChild("HumanoidRootPart")
|
|
if root then
|
|
hum:MoveTo(state.targetPoint)
|
|
if (root.Position - state.targetPoint).Magnitude < 5 then
|
|
state.targetPoint = patrolPoints[math.random(#patrolPoints)]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Detection loop
|
|
local function detectLoop()
|
|
while enemy.Parent and hum.Health > 0 do
|
|
task.wait(0.3)
|
|
local root = enemy:FindFirstChild("HumanoidRootPart")
|
|
if not root then continue end
|
|
for _, player in ipairs(Players:GetPlayers()) do
|
|
if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
|
|
local pRoot = player.Character.HumanoidRootPart
|
|
local dist = (pRoot.Position - root.Position).Magnitude
|
|
if dist < state.detectRange then
|
|
state.status = "chase"
|
|
hum:MoveTo(pRoot.Position)
|
|
if dist < state.attackRange and tick() - state.lastShot > 1.5 then
|
|
state.lastShot = tick()
|
|
-- Damage player
|
|
local pHum = player.Character:FindFirstChild("Humanoid")
|
|
if pHum and pHum.Health > 0 then
|
|
pHum:TakeDamage(8 + math.random(7))
|
|
DamageEvent:FireClient(player, 10)
|
|
end
|
|
end
|
|
elseif dist > state.detectRange * 1.5 then
|
|
state.status = "patrol"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Death handler
|
|
hum.Died:Connect(function()
|
|
for _, p in ipairs(Players:GetPlayers()) do
|
|
local char = p.Character
|
|
if char and char:FindFirstChild("HumanoidRootPart") then
|
|
local dist = (enemy:FindFirstChild("HumanoidRootPart") and enemy.HumanoidRootPart.Position or Vector3.zero) - char.HumanoidRootPart.Position
|
|
if dist.Magnitude < 80 then
|
|
EnemyKilledEvent:FireClient(p, enemy.Name)
|
|
end
|
|
end
|
|
end
|
|
task.delay(8, function()
|
|
if enemy and enemy.Parent then enemy:Destroy() end
|
|
end)
|
|
-- Respawn new enemy
|
|
task.delay(12, function()
|
|
if #Workspace:GetChildren() - 10 < MAX_ENEMIES then
|
|
local sp = ENEMY_SPawns[math.random(#ENEMY_SPawns)]
|
|
createEnemy(sp)
|
|
end
|
|
end)
|
|
end)
|
|
|
|
coroutine.wrap(patrolLoop)()
|
|
coroutine.wrap(detectLoop)()
|
|
return state
|
|
end
|
|
|
|
-- Spawn initial enemies
|
|
for i = 1, MAX_ENEMIES do
|
|
task.delay(i * 0.5, function()
|
|
createEnemy(ENEMY_SPawns[i])
|
|
end)
|
|
end
|
|
|
|
print("[GameServer] 20-Weapon FPS Game loaded!")
|
|
]]
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 4: HUD (StarterGui)
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local hud = Instance.new("ScreenGui", game.StarterGui)
|
|
hud.Name = "FPS_HUD"
|
|
hud.ResetOnSpawn = false
|
|
hud.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
|
|
|
-- Crosshair
|
|
local cross = Instance.new("Frame", hud)
|
|
cross.Name = "Crosshair"
|
|
cross.Size = UDim2.new(0, 20, 0, 2)
|
|
cross.Position = UDim2.new(0.5, -10, 0.5, -1)
|
|
cross.BackgroundColor3 = Color3.new(1, 1, 1)
|
|
cross.BorderSizePixel = 0
|
|
|
|
local crossV = Instance.new("Frame", hud)
|
|
crossV.Name = "CrosshairV"
|
|
crossV.Size = UDim2.new(0, 2, 0, 20)
|
|
crossV.Position = UDim2.new(0.5, -1, 0.5, -10)
|
|
crossV.BackgroundColor3 = Color3.new(1, 1, 1)
|
|
crossV.BorderSizePixel = 0
|
|
|
|
-- Hit Marker
|
|
local hm = Instance.new("Frame", hud)
|
|
hm.Name = "HitMarker"
|
|
hm.Size = UDim2.new(0, 30, 0, 30)
|
|
hm.Position = UDim2.new(0.5, -15, 0.5, -15)
|
|
hm.BackgroundTransparency = 1
|
|
hm.Visible = false
|
|
|
|
local hm1 = Instance.new("Frame", hm)
|
|
hm1.Size = UDim2.new(0, 15, 0, 3)
|
|
hm1.Position = UDim2.new(0, 15, 0, 0)
|
|
hm1.Rotation = 45
|
|
hm1.BackgroundColor3 = Color3.new(1, 1, 1)
|
|
hm1.BorderSizePixel = 0
|
|
|
|
local hm2 = Instance.new("Frame", hm)
|
|
hm2.Size = UDim2.new(0, 15, 0, 3)
|
|
hm2.Position = UDim2.new(0, 0, 0, 15)
|
|
hm2.Rotation = 45
|
|
hm2.BackgroundColor3 = Color3.new(1, 1, 1)
|
|
hm2.BorderSizePixel = 0
|
|
|
|
-- Health Bar (bottom left)
|
|
local hbFrame = Instance.new("Frame", hud)
|
|
hbFrame.Name = "HealthFrame"
|
|
hbFrame.Size = UDim2.new(0, 250, 0, 30)
|
|
hbFrame.Position = UDim2.new(0, 20, 1, -60)
|
|
hbFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
|
|
hbFrame.BorderSizePixel = 0
|
|
hbFrame.BackgroundTransparency = 0.3
|
|
|
|
local hbFill = Instance.new("Frame", hbFrame)
|
|
hbFill.Name = "HealthFill"
|
|
hbFill.Size = UDim2.new(1, 0, 1, 0)
|
|
hbFill.BackgroundColor3 = Color3.fromRGB(0, 200, 0)
|
|
hbFill.BorderSizePixel = 0
|
|
|
|
local hbText = Instance.new("TextLabel", hbFrame)
|
|
hbText.Name = "HealthText"
|
|
hbText.Size = UDim2.new(1, 0, 1, 0)
|
|
hbText.BackgroundTransparency = 1
|
|
hbText.TextColor3 = Color3.new(1, 1, 1)
|
|
hbText.TextStrokeTransparency = 0.5
|
|
hbText.Font = Enum.Font.GothamBold
|
|
hbText.TextSize = 16
|
|
hbText.Text = "100 HP"
|
|
|
|
-- Ammo display (bottom right)
|
|
local ammoFrame = Instance.new("Frame", hud)
|
|
ammoFrame.Name = "AmmoFrame"
|
|
ammoFrame.Size = UDim2.new(0, 200, 0, 60)
|
|
ammoFrame.Position = UDim2.new(1, -220, 1, -80)
|
|
ammoFrame.BackgroundTransparency = 1
|
|
|
|
local ammoText = Instance.new("TextLabel", ammoFrame)
|
|
ammoText.Name = "AmmoText"
|
|
ammoText.Size = UDim2.new(1, 0, 0.6, 0)
|
|
ammoText.Position = UDim2.new(0, 0, 0, 0)
|
|
ammoText.BackgroundTransparency = 1
|
|
ammoText.TextColor3 = Color3.new(1, 1, 1)
|
|
ammoText.TextStrokeTransparency = 0.5
|
|
ammoText.Font = Enum.Font.GothamBold
|
|
ammoText.TextSize = 28
|
|
ammoText.TextXAlignment = Enum.TextXAlignment.Right
|
|
ammoText.Text = "30 / 30"
|
|
|
|
local weaponName = Instance.new("TextLabel", ammoFrame)
|
|
weaponName.Name = "WeaponName"
|
|
weaponName.Size = UDim2.new(1, 0, 0.4, 0)
|
|
weaponName.Position = UDim2.new(0, 0, 0.6, 0)
|
|
weaponName.BackgroundTransparency = 1
|
|
weaponName.TextColor3 = Color3.fromRGB(200, 200, 200)
|
|
weaponName.TextStrokeTransparency = 0.7
|
|
weaponName.Font = Enum.Font.Gotham
|
|
weaponName.TextSize = 14
|
|
weaponName.TextXAlignment = Enum.TextXAlignment.Right
|
|
weaponName.Text = "M4A1 Carbine"
|
|
|
|
-- Reload bar
|
|
local reloadBar = Instance.new("Frame", hud)
|
|
reloadBar.Name = "ReloadBar"
|
|
reloadBar.Size = UDim2.new(0, 200, 0, 6)
|
|
reloadBar.Position = UDim2.new(0.5, -100, 1, -120)
|
|
reloadBar.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
|
|
reloadBar.BorderSizePixel = 0
|
|
reloadBar.Visible = false
|
|
reloadBar.BackgroundTransparency = 0.3
|
|
|
|
local reloadFill = Instance.new("Frame", reloadBar)
|
|
reloadFill.Name = "Fill"
|
|
reloadFill.Size = UDim2.new(0, 0, 1, 0)
|
|
reloadFill.BackgroundColor3 = Color3.fromRGB(255, 200, 0)
|
|
reloadFill.BorderSizePixel = 0
|
|
|
|
local reloadText = Instance.new("TextLabel", hud)
|
|
reloadText.Name = "ReloadText"
|
|
reloadText.Size = UDim2.new(0, 200, 0, 20)
|
|
reloadText.Position = UDim2.new(0.5, -100, 1, -140)
|
|
reloadText.BackgroundTransparency = 1
|
|
reloadText.TextColor3 = Color3.fromRGB(255, 200, 0)
|
|
reloadText.Font = Enum.Font.GothamBold
|
|
reloadText.TextSize = 14
|
|
reloadText.Text = "RELOADING..."
|
|
reloadText.Visible = false
|
|
|
|
-- Kill Feed (top right)
|
|
local kf = Instance.new("Frame", hud)
|
|
kf.Name = "KillFeed"
|
|
kf.Size = UDim2.new(0, 250, 0, 200)
|
|
kf.Position = UDim2.new(1, -260, 0, 10)
|
|
kf.BackgroundTransparency = 1
|
|
|
|
local kfLayout = Instance.new("UIListLayout", kf)
|
|
kfLayout.SortOrder = Enum.SortOrder.LayoutOrder
|
|
kfLayout.Padding = UDim.new(0, 4)
|
|
kfLayout.VerticalAlignment = Enum.VerticalAlignment.Top
|
|
|
|
-- Score display (top center)
|
|
local scoreFrame = Instance.new("Frame", hud)
|
|
scoreFrame.Name = "ScoreFrame"
|
|
scoreFrame.Size = UDim2.new(0, 200, 0, 40)
|
|
scoreFrame.Position = UDim2.new(0.5, -100, 0, 10)
|
|
scoreFrame.BackgroundColor3 = Color3.fromRGB(20, 20, 20)
|
|
scoreFrame.BackgroundTransparency = 0.5
|
|
scoreFrame.BorderSizePixel = 0
|
|
|
|
local scoreText = Instance.new("TextLabel", scoreFrame)
|
|
scoreText.Name = "ScoreText"
|
|
scoreText.Size = UDim2.new(1, 0, 1, 0)
|
|
scoreText.BackgroundTransparency = 1
|
|
scoreText.TextColor3 = Color3.new(1, 1, 1)
|
|
scoreText.Font = Enum.Font.GothamBold
|
|
scoreText.TextSize = 20
|
|
scoreText.Text = "KILLS: 0"
|
|
|
|
-- Streak banner (center)
|
|
local streak = Instance.new("TextLabel", hud)
|
|
streak.Name = "StreakBanner"
|
|
streak.Size = UDim2.new(0, 400, 0, 60)
|
|
streak.Position = UDim2.new(0.5, -200, 0.3, 0)
|
|
streak.BackgroundTransparency = 1
|
|
streak.TextColor3 = Color3.fromRGB(255, 50, 50)
|
|
streak.Font = Enum.Font.GothamBold
|
|
streak.TextSize = 36
|
|
streak.TextStrokeTransparency = 0.3
|
|
streak.Text = ""
|
|
streak.Visible = false
|
|
|
|
-- Damage Vignette
|
|
local vignette = Instance.new("ImageLabel", hud)
|
|
vignette.Name = "DamageVignette"
|
|
vignette.Size = UDim2.new(1, 0, 1, 0)
|
|
vignette.Position = UDim2.new(0, 0, 0, 0)
|
|
vignette.BackgroundTransparency = 1
|
|
vignette.ImageTransparency = 1
|
|
vignette.Image = "rbxassetid://1489679531"
|
|
vignette.ImageColor3 = Color3.new(1, 0, 0)
|
|
vignette.ScaleType = Enum.ScaleType.Slice
|
|
vignette.SliceCenter = Rect.new(0.4, 0.4, 0.6, 0.6)
|
|
|
|
-- Minimap (top left)
|
|
local minimap = Instance.new("Frame", hud)
|
|
minimap.Name = "Minimap"
|
|
minimap.Size = UDim2.new(0, 150, 0, 150)
|
|
minimap.Position = UDim2.new(0, 10, 0, 10)
|
|
minimap.BackgroundColor3 = Color3.fromRGB(20, 20, 30)
|
|
minimap.BorderSizePixel = 2
|
|
minimap.BorderColor3 = Color3.fromRGB(100, 100, 100)
|
|
|
|
local mapIcon = Instance.new("Frame", minimap)
|
|
mapIcon.Name = "PlayerDot"
|
|
mapIcon.Size = UDim2.new(0, 6, 0, 6)
|
|
mapIcon.Position = UDim2.new(0.5, -3, 0.5, -3)
|
|
mapIcon.BackgroundColor3 = Color3.fromRGB(0, 150, 255)
|
|
mapIcon.BorderSizePixel = 0
|
|
mapIcon.Shape = Enum.UIRectShape.Circle
|
|
|
|
-- Weapon list (bottom center)
|
|
local weaponList = Instance.new("Frame", hud)
|
|
weaponList.Name = "WeaponList"
|
|
weaponList.Size = UDim2.new(0, 500, 0, 25)
|
|
weaponList.Position = UDim2.new(0.5, -250, 1, -25)
|
|
weaponList.BackgroundColor3 = Color3.fromRGB(15, 15, 15)
|
|
weaponList.BackgroundTransparency = 0.5
|
|
weaponList.BorderSizePixel = 0
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 5: PLAYER SETUP (StarterPlayerScripts)
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local ps = Instance.new("LocalScript", game.StarterPlayer.StarterPlayerScripts)
|
|
ps.Name = "PlayerSetup"
|
|
ps.Source = [[
|
|
local Players = game:GetService("Players")
|
|
local RunService = game:GetService("RunService")
|
|
local UserInputService = game:GetService("UserInputService")
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
local TweenService = game:GetService("TweenService")
|
|
|
|
local player = Players.LocalPlayer
|
|
local camera = workspace.CurrentCamera
|
|
|
|
player.CharacterAdded:Connect(function(character)
|
|
local humanoid = character:WaitForChild("Humanoid")
|
|
local rootPart = character:WaitForChild("HumanoidRootPart")
|
|
|
|
-- First person camera
|
|
RunService.RenderStepped:Connect(function()
|
|
if humanoid.Health > 0 then
|
|
camera.CameraType = Enum.CameraType.LockFirstPerson
|
|
local head = character:FindFirstChild("Head")
|
|
if head then
|
|
camera.CFrame = head.CFrame
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Sprint / Crouch
|
|
local sprinting = false
|
|
local crouching = false
|
|
|
|
UserInputService.InputBegan:Connect(function(input, gpe)
|
|
if gpe then return end
|
|
if input.KeyCode == Enum.KeyCode.LeftShift then
|
|
sprinting = true
|
|
humanoid.WalkSpeed = 28
|
|
elseif input.KeyCode == Enum.KeyCode.LeftControl then
|
|
crouching = true
|
|
humanoid.WalkSpeed = 8
|
|
end
|
|
end)
|
|
|
|
UserInputService.InputEnded:Connect(function(input)
|
|
if input.KeyCode == Enum.KeyCode.LeftShift then
|
|
sprinting = false
|
|
humanoid.WalkSpeed = crouching and 8 or 18
|
|
elseif input.KeyCode == Enum.KeyCode.LeftControl then
|
|
crouching = false
|
|
humanoid.WalkSpeed = sprinting and 28 or 18
|
|
end
|
|
end)
|
|
end)
|
|
|
|
-- Kill Feed listener
|
|
local Events = ReplicatedStorage:WaitForChild("Events")
|
|
local KillEvent = Events:WaitForChild("KillEvent")
|
|
local DamageEvent = Events:WaitForChild("DamageEvent")
|
|
local HitMarkerEvent = Events:WaitForChild("HitMarkerEvent")
|
|
|
|
KillEvent.OnClientEvent:Connect(function(killer)
|
|
local hud = player:WaitForChild("PlayerGui"):WaitForChild("FPS_HUD")
|
|
local kf = hud:FindFirstChild("KillFeed")
|
|
|
|
local entry = Instance.new("TextLabel", kf)
|
|
entry.Size = UDim2.new(1, 0, 0, 20)
|
|
entry.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
|
|
entry.BackgroundTransparency = 0.5
|
|
entry.TextColor3 = Color3.new(1, 1, 1)
|
|
entry.Font = Enum.Font.GothamBold
|
|
entry.TextSize = 13
|
|
entry.Text = player.Name .. " eliminated " .. (killer or "Enemy")
|
|
entry.TextXAlignment = Enum.TextXAlignment.Right
|
|
|
|
game:GetService("Debris"):AddItem(entry, 5)
|
|
end)
|
|
|
|
-- Damage vignette
|
|
DamageEvent.OnClientEvent:Connect(function(dmg)
|
|
local hud = player:WaitForChild("PlayerGui"):WaitForChild("FPS_HUD")
|
|
local vig = hud:FindFirstChild("DamageVignette")
|
|
if vig then
|
|
vig.ImageTransparency = 0.3
|
|
local t = TweenService:Create(vig, TweenInfo.new(0.8), {ImageTransparency = 1})
|
|
t:Play()
|
|
end
|
|
end)
|
|
|
|
-- Hit marker flash
|
|
HitMarkerEvent.OnClientEvent:Connect(function(isHeadshot)
|
|
local hud = player:WaitForChild("PlayerGui"):WaitForChild("FPS_HUD")
|
|
local hm = hud:FindFirstChild("HitMarker")
|
|
if hm then
|
|
hm.Visible = true
|
|
if isHeadshot then
|
|
for _, c in pairs(hm:GetChildren()) do
|
|
c.BackgroundColor3 = Color3.fromRGB(255, 50, 50)
|
|
end
|
|
else
|
|
for _, c in pairs(hm:GetChildren()) do
|
|
c.BackgroundColor3 = Color3.new(1, 1, 1)
|
|
end
|
|
end
|
|
task.delay(0.15, function()
|
|
hm.Visible = false
|
|
end)
|
|
end
|
|
end)
|
|
|
|
-- Minimap updater
|
|
RunService.RenderStepped:Connect(function()
|
|
local hud = player.PlayerGui:FindFirstChild("FPS_HUD")
|
|
if not hud then return end
|
|
local minimap = hud:FindFirstChild("Minimap")
|
|
if not minimap then return end
|
|
|
|
local character = player.Character
|
|
if not character or not character:FindFirstChild("HumanoidRootPart") then return end
|
|
|
|
local pos = character.HumanoidRootPart.Position
|
|
local mapSize = 400
|
|
local mmSize = 150
|
|
|
|
-- Clear old enemy dots
|
|
for _, c in pairs(minimap:GetChildren()) do
|
|
if c.Name == "EnemyDot" then c:Destroy() end
|
|
end
|
|
|
|
-- Player dot
|
|
local dot = minimap:FindFirstChild("PlayerDot")
|
|
if dot then
|
|
local rx = (pos.X / mapSize + 0.5) * mmSize
|
|
local rz = (pos.Z / mapSize + 0.5) * mmSize
|
|
dot.Position = UDim2.new(0, rx - 3, 0, rz - 3)
|
|
end
|
|
|
|
-- Enemy dots
|
|
for _, obj in pairs(workspace:GetChildren()) do
|
|
if obj:IsA("Model") and obj.Name == "EnemySoldier" and obj:FindFirstChild("HumanoidRootPart") then
|
|
local epos = obj.HumanoidRootPart.Position
|
|
local dist = (epos - pos).Magnitude
|
|
if dist < 100 then
|
|
local ex = (epos.X / mapSize + 0.5) * mmSize
|
|
local ez = (epos.Z / mapSize + 0.5) * mmSize
|
|
local eDot = Instance.new("Frame", minimap)
|
|
eDot.Name = "EnemyDot"
|
|
eDot.Size = UDim2.new(0, 4, 0, 4)
|
|
eDot.Position = UDim2.new(0, ex - 2, 0, ez - 2)
|
|
eDot.BackgroundColor3 = Color3.new(1, 0, 0)
|
|
eDot.BorderSizePixel = 0
|
|
eDot.Shape = Enum.UIRectShape.Circle
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Health bar update
|
|
local char = player.Character
|
|
if char and char:FindFirstChild("Humanoid") then
|
|
local h = char.Humanoid
|
|
local hFrame = hud:FindFirstChild("HealthFrame")
|
|
if hFrame then
|
|
local fill = hFrame:FindFirstChild("HealthFill")
|
|
local txt = hFrame:FindFirstChild("HealthText")
|
|
if fill then
|
|
fill.Size = UDim2.new(h.Health / h.MaxHealth, 0, 1, 0)
|
|
if h.Health > 60 then fill.BackgroundColor3 = Color3.fromRGB(0, 200, 0)
|
|
elseif h.Health > 30 then fill.BackgroundColor3 = Color3.fromRGB(255, 200, 0)
|
|
else fill.BackgroundColor3 = Color3.fromRGB(255, 0, 0) end
|
|
end
|
|
if txt then txt.Text = math.floor(h.Health) .. " HP" end
|
|
end
|
|
end
|
|
end)
|
|
|
|
print("[PlayerSetup] Loaded!")
|
|
]]
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- PART 6: WEAPON CONTROLLER (StarterPlayerScripts) - 20 WEAPONS
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local wc = Instance.new("LocalScript", game.StarterPlayer.StarterPlayerScripts)
|
|
wc.Name = "WeaponController"
|
|
wc.Source = [[
|
|
local Players = game:GetService("Players")
|
|
local RunService = game:GetService("RunService")
|
|
local UserInputService = game:GetService("UserInputService")
|
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|
local TweenService = game:GetService("TweenService")
|
|
local Debris = game:GetService("Debris")
|
|
|
|
local player = Players.LocalPlayer
|
|
local camera = workspace.CurrentCamera
|
|
local mouse = player:GetMouse()
|
|
|
|
local Events = ReplicatedStorage:WaitForChild("Events")
|
|
local HitEvent = Events:WaitForChild("HitEvent")
|
|
local KillEvent = Events:WaitForChild("KillEvent")
|
|
local WeaponSwitchEvent = Events:WaitForChild("WeaponSwitchEvent")
|
|
|
|
local WeaponData = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("WeaponData"))
|
|
|
|
-- State
|
|
local currentIndex = 1
|
|
local currentAmmo = {}
|
|
local isReloading = false
|
|
local isADS = false
|
|
local canShoot = true
|
|
local recoilOffset = 0
|
|
local spreadOffset = 0
|
|
local lastShotTime = 0
|
|
local kills = 0
|
|
local killStreak = 0
|
|
local lastKillTime = 0
|
|
|
|
-- Initialize ammo for all weapons
|
|
for i, w in ipairs(WeaponData) do
|
|
currentAmmo[i] = w.MagSize
|
|
end
|
|
|
|
-- Key map
|
|
local keyMap = {}
|
|
for i, w in ipairs(WeaponData) do
|
|
keyMap[w.Key] = i
|
|
end
|
|
|
|
-- ═══════ WEAPON SWITCHING ═══════
|
|
local function switchWeapon(index)
|
|
if index < 1 or index > #WeaponData or isReloading then return end
|
|
currentIndex = index
|
|
local w = WeaponData[currentIndex]
|
|
|
|
-- Update HUD
|
|
local hud = player.PlayerGui:FindFirstChild("FPS_HUD")
|
|
if hud then
|
|
local ammoText = hud:FindFirstChild("AmmoFrame"):FindFirstChild("AmmoText")
|
|
local wnText = hud:FindFirstChild("AmmoFrame"):FindFirstChild("WeaponName")
|
|
if ammoText then ammoText.Text = currentAmmo[currentIndex] .. " / " .. w.MagSize end
|
|
if wnText then wnText.Text = w.Name end
|
|
end
|
|
|
|
isADS = false
|
|
camera.FieldOfView = 70
|
|
recoilOffset = 0
|
|
spreadOffset = 0
|
|
end
|
|
|
|
UserInputService.InputBegan:Connect(function(input, gpe)
|
|
if gpe then return end
|
|
|
|
-- Weapon keys
|
|
if keyMap[input.KeyCode] then
|
|
switchWeapon(keyMap[input.KeyCode])
|
|
end
|
|
|
|
-- ADS (Right mouse button)
|
|
if input.UserInputType == Enum.UserInputType.MouseButton2 then
|
|
local w = WeaponData[currentIndex]
|
|
if w.ScopeZoom then
|
|
isADS = true
|
|
camera.FieldOfView = 70 / w.ScopeZoom
|
|
elseif isADS == false then
|
|
isADS = true
|
|
local t = TweenService:Create(camera, TweenInfo.new(0.15), {FieldOfView = 45})
|
|
t:Play()
|
|
end
|
|
end
|
|
|
|
-- Reload
|
|
if input.KeyCode == Enum.KeyCode.R and not isReloading then
|
|
local w = WeaponData[currentIndex]
|
|
if currentAmmo[currentIndex] < w.MagSize then
|
|
isReloading = true
|
|
canShoot = false
|
|
|
|
local hud = player.PlayerGui:FindFirstChild("FPS_HUD")
|
|
if hud then
|
|
hud.ReloadBar.Visible = true
|
|
hud.ReloadText.Visible = true
|
|
end
|
|
|
|
-- Animate reload bar
|
|
local bar = hud and hud:FindFirstChild("ReloadBar")
|
|
local fill = bar and bar:FindFirstChild("Fill")
|
|
if fill then
|
|
fill.Size = UDim2.new(0, 0, 1, 0)
|
|
local rt = TweenService:Create(fill, TweenInfo.new(w.ReloadTime), {Size = UDim2.new(1, 0, 1, 0)})
|
|
rt:Play()
|
|
end
|
|
|
|
task.delay(w.ReloadTime, function()
|
|
currentAmmo[currentIndex] = w.MagSize
|
|
isReloading = false
|
|
canShoot = true
|
|
if hud then
|
|
hud.ReloadBar.Visible = false
|
|
hud.ReloadText.Visible = false
|
|
local ammoText = hud:FindFirstChild("AmmoFrame"):FindFirstChild("AmmoText")
|
|
if ammoText then ammoText.Text = currentAmmo[currentIndex] .. " / " .. w.MagSize end
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
end)
|
|
|
|
UserInputService.InputEnded:Connect(function(input)
|
|
if input.UserInputType == Enum.UserInputType.MouseButton2 then
|
|
isADS = false
|
|
local t = TweenService:Create(camera, TweenInfo.new(0.15), {FieldOfView = 70})
|
|
t:Play()
|
|
end
|
|
end)
|
|
|
|
-- ═══════ SHOOTING ═══════
|
|
local function shoot()
|
|
local w = WeaponData[currentIndex]
|
|
if not canShoot or isReloading then return end
|
|
if currentAmmo[currentIndex] <= 0 then return end
|
|
|
|
currentAmmo[currentIndex] = currentAmmo[currentIndex] - 1
|
|
|
|
-- Update ammo HUD
|
|
local hud = player.PlayerGui:FindFirstChild("FPS_HUD")
|
|
if hud then
|
|
local ammoText = hud:FindFirstChild("AmmoFrame"):FindFirstChild("AmmoText")
|
|
if ammoText then ammoText.Text = currentAmmo[currentIndex] .. " / " .. w.MagSize end
|
|
end
|
|
|
|
-- Raycast
|
|
local character = player.Character
|
|
if not character or not character:FindFirstChild("Head") then return end
|
|
|
|
local head = character.Head
|
|
local spread = isADS and w.ADS_Spread or w.Spread
|
|
spread = spread + spreadOffset
|
|
|
|
local rayOrigin = head.Position
|
|
local rayDir = (mouse.Hit.Position - rayOrigin).Unit
|
|
-- Add spread
|
|
rayDir = rayDir + Vector3.new(
|
|
(math.random() - 0.5) * spread,
|
|
(math.random() - 0.5) * spread,
|
|
(math.random() - 0.5) * spread
|
|
)
|
|
rayDir = rayDir.Unit
|
|
|
|
local rayParams = RaycastParams.new()
|
|
rayParams.FilterType = Enum.RaycastFilterType.Exclude
|
|
rayParams.FilterDescendantsInstances = {character}
|
|
|
|
-- Shotgun pellets
|
|
local pellets = w.Pellets or 1
|
|
for p = 1, pellets do
|
|
local pDir = rayDir
|
|
if pellets > 1 then
|
|
pDir = pDir + Vector3.new(
|
|
(math.random() - 0.5) * w.Spread * 2,
|
|
(math.random() - 0.5) * w.Spread * 2,
|
|
(math.random() - 0.5) * w.Spread * 2
|
|
)
|
|
pDir = pDir.Unit
|
|
end
|
|
|
|
local result = workspace:Raycast(rayOrigin, pDir * w.Range, rayParams)
|
|
if result then
|
|
local hitPart = result.Instance
|
|
local isHeadshot = hitPart.Name == "Head"
|
|
|
|
-- Bullet trail
|
|
local trail = Instance.new("Part", workspace)
|
|
trail.Size = Vector3.new(0.1, 0.1, (result.Position - rayOrigin).Magnitude)
|
|
trail.CFrame = CFrame.lookAt(rayOrigin, result.Position) * CFrame.new(0, 0, -trail.Size.Z / 2)
|
|
trail.Anchored = true
|
|
trail.CanCollide = false
|
|
trail.BrickColor = BrickColor.new("Bright yellow")
|
|
trail.Material = Enum.Material.Neon
|
|
trail.Transparency = 0.3
|
|
Debris:AddItem(trail, 0.15)
|
|
|
|
-- Impact spark
|
|
local spark = Instance.new("Part", workspace)
|
|
spark.Size = Vector3.new(0.5, 0.5, 0.5)
|
|
spark.Position = result.Position
|
|
spark.Anchored = true
|
|
spark.CanCollide = false
|
|
spark.BrickColor = BrickColor.new("Bright orange")
|
|
spark.Material = Enum.Material.Neon
|
|
Debris:AddItem(spark, 0.1)
|
|
|
|
-- Check if enemy
|
|
if hitPart.Parent and hitPart.Parent:FindFirstChild("Humanoid") then
|
|
local hum = hitPart.Parent:FindFirstChild("Humanoid")
|
|
if hum and hum.Health > 0 then
|
|
local dmg = isHeadshot and w.Damage * w.HeadMulti or w.Damage
|
|
if w.Explosive then
|
|
dmg = w.Damage
|
|
-- Explosion
|
|
local explosion = Instance.new("Explosion", workspace)
|
|
explosion.Position = result.Position
|
|
explosion.BlastRadius = w.BlastRadius or 20
|
|
explosion.BlastPressure = 500000
|
|
end
|
|
|
|
HitEvent:FireServer(hitPart, dmg, isHeadshot, w.Name)
|
|
|
|
if hum.Health <= 0 then
|
|
kills = kills + 1
|
|
killStreak = killStreak + 1
|
|
lastKillTime = tick()
|
|
|
|
-- Update score
|
|
if hud then
|
|
local st = hud:FindFirstChild("ScoreFrame"):FindFirstChild("ScoreText")
|
|
if st then st.Text = "KILLS: " .. kills end
|
|
|
|
-- Kill streak banner
|
|
local banner = hud:FindFirstChild("StreakBanner")
|
|
if banner then
|
|
local streaks = {
|
|
{2, "DOUBLE KILL!"},
|
|
{3, "TRIPLE KILL!"},
|
|
{5, "KILLING SPREE!"},
|
|
{7, "UNSTOPPABLE!"},
|
|
{10, "GODLIKE!"},
|
|
}
|
|
for _, s in ipairs(streaks) do
|
|
if killStreak == s[1] then
|
|
banner.Text = s[2]
|
|
banner.Visible = true
|
|
task.delay(2, function()
|
|
banner.Visible = false
|
|
end)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
KillEvent:FireServer("EnemySoldier")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Recoil
|
|
recoilOffset = math.min(recoilOffset + w.Recoil, w.Recoil * 5)
|
|
spreadOffset = math.min(spreadOffset + w.Spread * 0.5, w.Spread * 3)
|
|
|
|
-- Auto reload when empty
|
|
if currentAmmo[currentIndex] <= 0 then
|
|
task.delay(0.3, function()
|
|
if currentAmmo[currentIndex] <= 0 and not isReloading then
|
|
-- Trigger reload
|
|
isReloading = true
|
|
canShoot = false
|
|
local ww = WeaponData[currentIndex]
|
|
if hud then
|
|
hud.ReloadBar.Visible = true
|
|
hud.ReloadText.Visible = true
|
|
end
|
|
local fill = hud and hud:FindFirstChild("ReloadBar") and hud.ReloadBar:FindFirstChild("Fill")
|
|
if fill then
|
|
fill.Size = UDim2.new(0, 0, 1, 0)
|
|
TweenService:Create(fill, TweenInfo.new(ww.ReloadTime), {Size = UDim2.new(1, 0, 1, 0)}):Play()
|
|
end
|
|
task.delay(ww.ReloadTime, function()
|
|
currentAmmo[currentIndex] = ww.MagSize
|
|
isReloading = false
|
|
canShoot = true
|
|
if hud then
|
|
hud.ReloadBar.Visible = false
|
|
hud.ReloadText.Visible = false
|
|
local at = hud:FindFirstChild("AmmoFrame"):FindFirstChild("AmmoText")
|
|
if at then at.Text = currentAmmo[currentIndex] .. " / " .. ww.MagSize end
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
-- ═══════ FIRE RATE CONTROL ═══════
|
|
local holdingMouse = false
|
|
|
|
UserInputService.InputBegan:Connect(function(input, gpe)
|
|
if gpe then return end
|
|
if input.UserInputType == Enum.UserInputType.MouseButton1 then
|
|
holdingMouse = true
|
|
local w = WeaponData[currentIndex]
|
|
|
|
if w.Auto then
|
|
-- Auto fire loop
|
|
task.spawn(function()
|
|
while holdingMouse and canShoot do
|
|
shoot()
|
|
task.wait(w.FireRate)
|
|
end
|
|
end)
|
|
else
|
|
-- Semi-auto / burst
|
|
if w.BurstCount then
|
|
for b = 1, w.BurstCount do
|
|
shoot()
|
|
task.wait(w.FireRate)
|
|
end
|
|
else
|
|
shoot()
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
UserInputService.InputEnded:Connect(function(input)
|
|
if input.UserInputType == Enum.UserInputType.MouseButton1 then
|
|
holdingMouse = false
|
|
end
|
|
end)
|
|
|
|
-- ═══════ RECOIL / SPREAD DECAY ═══════
|
|
RunService.RenderStepped:Connect(function()
|
|
recoilOffset = math.max(recoilOffset - 0.15, 0)
|
|
spreadOffset = math.max(spreadOffset - 0.01, 0)
|
|
|
|
-- Crosshair spread visual
|
|
local hud = player.PlayerGui:FindFirstChild("FPS_HUD")
|
|
if hud then
|
|
local w = WeaponData[currentIndex]
|
|
local baseSpread = isADS and w.ADS_Spread or w.Spread
|
|
local totalSpread = baseSpread + spreadOffset
|
|
local offset = math.floor(totalSpread * 300)
|
|
|
|
local ch = hud:FindFirstChild("Crosshair")
|
|
local cv = hud:FindFirstChild("CrosshairV")
|
|
if ch then ch.Position = UDim2.new(0.5, -10 - offset, 0.5, -1) end
|
|
if cv then cv.Position = UDim2.new(0.5, -1, 0.5, -10 - offset) end
|
|
end
|
|
|
|
-- Kill streak decay
|
|
if tick() - lastKillTime > 5 and killStreak > 0 then
|
|
killStreak = 0
|
|
end
|
|
end)
|
|
|
|
-- Initialize first weapon display
|
|
switchWeapon(1)
|
|
|
|
print("[WeaponController] 20 weapons loaded!")
|
|
]]
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- WEAPON LIST HUD (keys display)
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
local weaponListFrame = hud:FindFirstChild("WeaponList")
|
|
if weaponListFrame then
|
|
local keyLabels = {
|
|
{key="1-6", label="ASSAULT RIFLES", color=Color3.fromRGB(100,150,255)},
|
|
{key="7-0", label="SMGS", color=Color3.fromRGB(100,255,100)},
|
|
{key="Q/E/T", label="SNIPERS", color=Color3.fromRGB(255,100,100)},
|
|
{key="Z/X", label="SHOTGUNS", color=Color3.fromRGB(255,200,50)},
|
|
{key="C/V", label="LMGS", color=Color3.fromRGB(200,100,255)},
|
|
{key="B/N/G", label="PISTOL/LAUNCHER", color=Color3.fromRGB(255,150,100)},
|
|
}
|
|
for i, kl in ipairs(keyLabels) do
|
|
local lbl = Instance.new("TextLabel", weaponListFrame)
|
|
lbl.Size = UDim2.new(1/#keyLabels, 0, 1, 0)
|
|
lbl.Position = UDim2.new((i-1)/#keyLabels, 0, 0, 0)
|
|
lbl.BackgroundColor3 = kl.color
|
|
lbl.BackgroundTransparency = 0.7
|
|
lbl.TextColor3 = Color3.new(1, 1, 1)
|
|
lbl.Font = Enum.Font.GothamBold
|
|
lbl.TextSize = 10
|
|
lbl.Text = "["..kl.key.."] "..kl.label
|
|
end
|
|
end
|
|
|
|
print("════════════════════════════════════════════")
|
|
print(" MINI CALL OF DUTY - 20 WEAPONS LOADED!")
|
|
print(" Press F5 (Play) to start the game")
|
|
print(" WASD=Move LMB=Shoot RMB=ADS")
|
|
print(" Shift=Sprint Ctrl=Crouch R=Reload")
|
|
print(" Keys 1-6 = Assault Rifles")
|
|
print(" Keys 7,8,9,0 = SMGs")
|
|
print(" Keys Q,E,T = Snipers")
|
|
print(" Keys Z,X = Shotguns")
|
|
print(" Keys C,V = LMGs")
|
|
print(" Keys B,N = Pistols")
|
|
print(" Key G = RPG-7 Launcher")
|
|
print("════════════════════════════════════════════")
|