The prompt
build me a combat system with a sword, health bar, damage numbers, and respawn42 seconds later: 5 files, 140 lines, one credit. Let's walk through what Forge AI actually produced and why it works on first try.
What Forge generated
Five scripts placed into the right Roblox services:
ServerScriptService/CombatServer.lua (84 lines)
ServerStorage/Weapons/ClassicSword.rbxm (mesh + Tool)
ReplicatedStorage/Remotes/CombatRemote (RemoteEvent)
StarterGui/HealthBarUI (Frame + script)
StarterPlayerScripts/CombatClient.lua (56 lines)
StarterGui/HUD/DamageNumbers (pooled labels)Plus one wired-up Tool that ends up in every player's Backpack on join. Plus respawn handling on the server.
CombatServer.lua — server-authoritative damage
The most important script in any combat system is the server-side damage handler. Client-trusted damage is the #1 anti-exploit failure in Roblox games. Forge writes this with validation by default:
--!strict
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CombatRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("CombatRemote")
local cooldowns: { [number]: number } = {}
local DAMAGE = 18
local COOLDOWN = 0.45
local RANGE = 8
CombatRemote.OnServerEvent:Connect(function(player, targetUserId)
local now = tick()
if cooldowns[player.UserId] and now - cooldowns[player.UserId] < COOLDOWN then return end
local attacker = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
local target = Players:GetPlayerByUserId(targetUserId)
local victim = target and target.Character and target.Character:FindFirstChild("HumanoidRootPart")
if not attacker or not victim then return end
if (attacker.Position - victim.Position).Magnitude > RANGE then return end
cooldowns[player.UserId] = now
local humanoid = victim.Parent:FindFirstChildOfClass("Humanoid")
if humanoid then humanoid:TakeDamage(DAMAGE) end
end)Three checks before any damage applies: cooldown (no spam), valid targets (attacker and victim both have HumanoidRootPart), and range (attacker within 8 studs of target). The client only fires the remote with a target ID — all damage math runs server-side.
CombatClient.lua — input + animation
The client script handles the swing animation and fires the remote when the sword connects. Forge's version uses Tool.Activated as the trigger and a magnitude check to find the closest valid target:
--!strict
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CombatRemote = ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("CombatRemote")
local player = Players.LocalPlayer
local tool: Tool? = nil
local function onActivated()
local char = player.Character
local hrp = char and char:FindFirstChild("HumanoidRootPart")
if not hrp then return end
-- Find closest other player within 8 studs
local closest, closestDist = nil, math.huge
for _, p in Players:GetPlayers() do
if p == player then continue end
local theirChar = p.Character
local theirHrp = theirChar and theirChar:FindFirstChild("HumanoidRootPart")
if theirHrp then
local d = (hrp.Position - theirHrp.Position).Magnitude
if d < closestDist and d <= 8 then closest, closestDist = p, d end
end
end
if closest then CombatRemote:FireServer(closest.UserId) end
end
player.CharacterAdded:Connect(function(char)
char.ChildAdded:Connect(function(child)
if child:IsA("Tool") and child.Name == "ClassicSword" then
tool = child
child.Activated:Connect(onActivated)
end
end)
end)Why it works on first try
Three things keep this from being a "compile in tester, fail in Studio" wall of code:
- **Type-safe.** Both scripts ship
--!strictwith proper type annotations on tables and parameters. luau-lsp catches mistakes before they ship. - No client trust. Damage, cooldown, range — all validated server-side. The client only sends "I want to hit this target."
- **Real services referenced.** Forge reads your hierarchy on every prompt.
ReplicatedStorage:WaitForChild("Remotes")works because Forge created the folder and RemoteEvent in the same generation.
What you'd add manually after
- **Animation.** Tool ships without a swing animation by default; load
rbxassetid://507770239or your own. - Sound effects. Hit / miss / equip sounds.
- Visual hit feedback. Forge ships pooled DamageNumbers but doesn't add hit sparks.
- Balance. Damage and cooldown numbers are starter values.
These are 30-minute polish jobs, not core architecture. The combat skeleton is in.
Run cost
| Item | Credits | USD equivalent |
|---|---|---|
| Sonnet generation (5 files) | 5 | ~$0.05 |
| Total | 5 | ~$0.05 |
A combat system for under a dime. Free tier ships with 20 credits — that's four full systems for free.
Try it
Sign up for 20 free credits. Install the Forge AI plugin inside Roblox Studio. Type the same prompt above, get the same five-file output. Compare with whatever you'd get from copy-paste-ChatGPT after 30 minutes of integration work.
For more on the broader plugin, see the Luau generator overview or the game builder page.
Related system guides
- How to make a Roblox combat system — the deep dive on this exact prompt
- How to make a Roblox FPS — server-authoritative hit re-cast, ammo, recoil
- How to make a Roblox boss fight — phase transitions, telegraphs, loot
- How to prevent exploits in Roblox — server-authoritative patterns
Browse all 12 system guides.