§ How to · with Picoo

How to make a Roblox magic spell system (with AI)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A Roblox spell system needs spells bound to keys, a resource that limits casting, per-spell cooldowns, hits the server decides, and a hotbar that shows all of it. Ask Picoo for magic spells and its ability_system recipe builds all five in one step, starting with four fire spells: Fireball, Flame Rush, Meteor Slam and Flamethrower.

7 files · 422 lines · one recipe call. Three scripts, a folder with two RemoteEvents, and a hotbar ScreenGui you can restyle in Edit mode.

Four spells, up to six

The default kit is Fireball (a projectile), Flame Rush (a dash), Meteor Slam (a slam that damages everyone around you) and Flamethrower (a beam). Ask for five or six and it adds Blazing Fury (a speed and jump buff) and Ember Mend (a 35 HP heal).

Seven elements

Fire is the default. Ask for water, ice, lightning, shadow, wind or nature (frost, thunder, dark, air and earth work too) and the spell names, emoji icons and effect color change to match, for example Water Bullet, Flowing Step, Whirlpool and Torrent.

Mana with regen

Each player has 100 Mana, regenerating 6 per second. The default spells cost 12, 15, 35 and 28. A cast without enough Mana is refused by the server, and the Mana bar flashes red.

Per-spell cooldowns

Fireball 1.5 s, Flame Rush 4 s, Meteor Slam 9 s, Flamethrower 6 s. The server keeps each player's cooldowns and ignores early casts, and each hotbar slot shows a dark veil and a countdown until the spell is ready. Cooldowns and Mana belong to the player, so respawning does not reset them.

Server-side hits

All damage is applied on the server. Fireball flies 95 studs a second for up to 180 studs and deals 18; Meteor Slam deals 40 to every Humanoid within 22 studs; Flamethrower raycasts 120 studs and deals 32 where it lands. Hits use GetPartBoundsInRadius and Raycast, so a target takes one hit per cast.

Hotbar that exists in Edit mode

StarterGui.AbilityHUD is built as real instances, so you can restyle it in Studio without pressing Play. Each slot shows the spell's icon, name and key. Press the key or tap the slot to cast, which also covers phone players.

Files Picoo ships for this prompt

7 files · 422 lines · one recipe call

ReplicatedStorage/AbilityConfig.lua

ModuleScript registry: one row per spell with key, kind, cooldown, Mana cost, damage, range and power, plus the Mana settings. Everything else reads from it.

53 lines

ServerScriptService/AbilityServer.lua

Server Script that checks every cast (cooldown, Mana, a 100 ms flood guard), regenerates Mana, and runs the projectile, slam, beam, buff and heal effects and their damage.

255 lines

StarterGui/AbilityHUD/AbilityClient.lua

LocalScript that binds the keys and slot taps, sends each cast with the camera's aim, runs the cooldown sweep and the Mana bar, and applies the dash once the server approves it.

114 lines

StarterGui/AbilityHUD

ScreenGui built as 48 instances with the default four spells: hotbar slots with icon, name, keycap and cooldown veil, and the Mana bar.

instance

ReplicatedStorage/AbilityRemotes

Folder holding the two RemoteEvents.

instance

ReplicatedStorage/AbilityRemotes/CastAbility

Client to server: which spell, and the aim direction.

instance

ReplicatedStorage/AbilityRemotes/AbilityState

Server to client: Mana updates, approved casts with their cooldown, and refusals.

instance

Sample output: ServerScriptService/AbilityServer.lua

--!strict
-- Generated by Picoo recipe: ability_system (fire) — THE AUTHORITY.
-- Cooldowns, Mana cost, damage and hit detection all live here. The
-- client may ASK to cast; it can never decide that a cast was legal, so a
-- replayed or spammed remote costs the exploiter nothing but a rejection.

-- (excerpt) every cast goes through this one gate:
castEvent.OnServerEvent:Connect(function(plr: Player, id: unknown, dir: unknown)
	if typeof(id) ~= "string" then return end
	-- flood guard: no legitimate client casts twice within 100 ms
	local now = os.clock()
	if lastCast[plr] and now - lastCast[plr] < 0.1 then return end
	lastCast[plr] = now

	local def = Config.byId(id)
	if not def then return end

	local char, hrp, hum = charOf(plr)
	if not (char and hrp and hum) then return end

	local cds = cooldowns[plr]
	if not cds then return end
	local readyAt = cds[id]
	if readyAt and now < readyAt then return end

	local have = resource[plr] or 0
	if have < def.cost then
		stateEvent:FireClient(plr, { kind = "denied", id = id, reason = "resource" })
		return
	end

	local aim: Vector3
	if typeof(dir) == "Vector3" and dir.Magnitude > 0.01 then
		aim = dir.Unit
	else
		aim = hrp.CFrame.LookVector
	end

	resource[plr] = have - def.cost
	cds[id] = now + def.cooldown
	stateEvent:FireClient(plr, { kind = "cast", id = id, cooldown = def.cooldown, power = def.power })
	stateEvent:FireClient(plr, { kind = "resource", value = resource[plr], max = MAX })

	if def.kind == "projectile" then
		castProjectile(plr, def, char, hrp, aim)
	elseif def.kind == "aoe" then
		castAoe(plr, def, char, hrp)
	elseif def.kind == "beam" then
		castBeam(plr, def, char, hrp, aim)
	elseif def.kind == "buff" then
		castBuff(plr, def, char, hum)
	elseif def.kind == "heal" then
		castHeal(plr, def, char, hrp, hum)
	end
	-- "dash" needs no server work beyond the approval above: the client owns
	-- its character's physics and applies the impulse when it sees kind="cast".
end)

Building a Roblox magic spell system

Magic spell systems are the engine of RPG combat in Roblox. A good one comes down to four things: clear feedback when a spell is ready, a resource that makes players choose, cooldowns the client cannot skip, and hits that feel fair. Picoo builds spell systems from a fixed recipe, ability_system, so the same settings produce the same working scripts every time.

The recipe splits the system into three layers. AbilityConfig, a ModuleScript in ReplicatedStorage, is the registry: one row per spell with its key, kind, cooldown, Mana cost, damage, range and power. AbilityServer, in ServerScriptService, is the authority. The AbilityHUD ScreenGui in StarterGui is the hotbar, and its LocalScript binds each spell's key with ContextActionService and makes each slot tappable.

The default kit is four fire spells. Fireball (Q) is a glowing ball that flies 95 studs a second for up to 180 studs and deals 18 to any Humanoid within 4.5 studs of it. Flame Rush (E) is a dash. Meteor Slam (R) deals 40 to every Humanoid within 22 studs of the caster. Flamethrower (F) raycasts 120 studs in the direction the camera faces and deals 32 around the point it hits. Ask for another element and the same kinds come back with new names, icons and colors, such as Ice Shard and Freeze Ray for ice.

Every cast goes through one server gate. It ignores a second cast within 100 ms, checks that the spell exists and the caster is alive, rejects a spell still on cooldown, and refuses a cast without enough Mana, telling the client so the Mana bar can flash red. Only then does it spend the Mana, start the cooldown and run the effect. Each player has 100 Mana that regenerates 6 per second. Damage uses GetPartBoundsInRadius and Raycast rather than Touched, which can fire many times per contact, so each cast hits a target once.

The dash is the one effect applied on the client. Each player's client controls their own character's physics, so after the server approves Flame Rush, the LocalScript pushes the character along the camera's flat look direction. The other effects are Neon parts that grow and fade in the element's color. Cast times, sounds, spell levels and a spellbook are follow-up prompts, built on top of the same registry.

See more on the Luau generator, the AI Luau script writer, the 3D model generator and the game builder, read how Picoo compares with ChatGPT, or browse the full blog.

Frequently asked

What does Picoo build when I ask for a spell system?+

Three scripts and the pieces they share. ReplicatedStorage.AbilityConfig is the registry of spells. ServerScriptService.AbilityServer checks every cast and applies the effects and damage. StarterGui.AbilityHUD is the hotbar and Mana bar, with a LocalScript inside it for keys, taps and the cooldown display. Two RemoteEvents in ReplicatedStorage.AbilityRemotes connect them.

How do I add or rebalance a spell?+

Edit AbilityConfig. Change a cooldown, cost, damage or range number and the server and the hotbar both use the new value. To add a spell, copy a row, give it a unique id and a free key, and set its kind to projectile, dash, aoe, beam, buff or heal. The key works straight away; to click or tap it, the hotbar also needs a matching Slot button.

Can players cheat cooldowns or Mana?+

Not by firing the remote. The client only asks to cast; AbilityServer checks that the spell exists, the caster is alive, the cooldown has passed and there is enough Mana, and it ignores any cast within 100 ms of the last one. Damage is applied on the server. The one piece that runs on the client is the dash push, because each player's client controls their own character's movement.

Does it hurt other players?+

Yes. Damage goes to any Humanoid in range except the caster, so it hits other players and NPCs alike. For teams or safe zones, ask for a friendly-fire check in a follow-up prompt.

Can I add cast times, sounds, spell levels or a spellbook?+

Yes, in a follow-up prompt. The recipe casts instantly and draws its effects with glowing Neon parts in the element's color. Ask for the next layer on top, for example 'add a 0.5 second cast time to Meteor Slam', 'play a sound on each cast' or 'let players level up their spells'.

Related Picoo prompts