Lua Scripting: Getting Started
What You'll Learn
This page teaches you to write your first Lua power for EliteMobs, from an empty file all the way to a working boss ability. By the end you will understand hooks, context, cooldowns, and the general shape of every Lua power file.
Once you are comfortable with the basics, continue with the companion pages:
- API Reference -- the full
context.*method reference - Examples & Patterns -- complete working powers you can study and adapt
- Enum Reference -- valid values for Particle, Sound, Material, and other string constants
Lua power files are currently experimental. Hook names, helper methods, and behavior can still change as EliteMobs evolves, so test carefully before using them on a production server.
Lua does not replace the existing eliteScript: YAML system.
- Use EliteScript when you want declarative, YAML-driven scripting with the existing Actions, Targets, Zones, Conditions, Cooldowns, and Relative Vectors pages.
- Use Lua power files when you want variables, loops, random selection, reusable helper functions, persistent per-boss state, and more traditional programming flow.
- Use the Lua script bridge when you want the best of both: Lua control flow combined with the same target, zone, particle, and relative-vector specs documented in the EliteScript pages.
What Lua Powers Are
Lua powers are standalone .lua files that live in the normal EliteMobs powers tree and are referenced exactly like normal power files.
How To Read This Page If You Are New To Programming
If you are new to scripting, treat a Lua power file like this:
- A Lua file returns one table.
- That table contains one or more named hooks such as
on_spawnoron_boss_damaged_by_player. - EliteMobs calls those hooks when something happens.
- Each hook receives
context. - You use
contextto inspect the boss, player, event, cooldowns, world, zones, or EliteScript bridge.
Quick meaning of the words used on this page:
- A
hookis a function EliteMobs runs for you when a specific event happens. - A
callbackis a function you pass into another helper so that helper can run your code later. - A
lazy keyis a value such ascontext.playerorcontext.eventthat EliteMobs only resolves when you actually access it, which is why some of them can benilin hooks where they do not apply.
The simplest workflow is:
- Start with one hook.
- Print or send one message.
- Add one cooldown.
- Add one world effect or one boss action.
- Only after that, add scheduling, state, and zones.
If you are using AI to generate powers, this page is intended to be the canonical exact-name reference for:
- allowed top-level keys
- hook names
contextkeys- method names
- callback names
- accepted literal values
- common object/table shapes
What Lua Powers Are Good At
Lua powers shine when you need:
- Attack rotations and random move pickers
- Persistent state between hooks with
context.state - Delayed and repeating actions without building everything out of YAML waits
- Custom helper functions shared across one file
- Complex branching that would be awkward in pure EliteScript
- Boss logic that still wants to reuse EliteScript targeting and zone definitions
If your power is mostly "trigger event -> do a few scripted actions", the existing EliteScript docs are still the fastest and clearest way to build it. If your power needs real program flow, Lua is the new tool for that job.
Who This Page Is For
This page is written for three kinds of readers:
- someone who already knows EliteScript and wants to learn Lua without learning "real programming" all at once
- someone who is new to EliteMobs scripting and needs a complete exact-name reference
- someone using AI to draft powers and needing enough detail to tell when the AI invented something fake
You do not need to become a full Lua developer before writing useful powers.
For most practical EliteMobs powers, the only things you really need are:
- how to place a valid hook in the returned table
- how to read values from
context - how to stop early with
if ... then return end - how to call a few helper methods exactly
- how to copy the right target and zone specs from the existing EliteScript docs
The goal of this page is not just to explain the API. The goal is to help you produce working powers first, then learn the system more deeply from the same page afterward.
Mental Model: EliteScript vs Lua
If you know EliteScript, this comparison is the fastest way to understand Lua powers:
| If you think in EliteScript terms | In Lua that usually means |
|---|---|
| Events | Hook names such as on_spawn or on_boss_damaged_by_player |
| Cooldowns | context.cooldowns (see context.cooldowns for full details) |
| Actions | Direct method calls such as context.world:spawn_particle_at_location(...) or context.script:damage(...) |
| Targets | context.script:target({...}) |
| Zones | context.script:zone({...}) or native context.zones helpers |
| Relative vectors | context.script:relative_vector({...}) or context.vectors |
| Script flow | Your own Lua if, helper functions, timers, and state |
The biggest difference is this:
- EliteScript says what should happen in YAML.
- Lua lets you decide when, why, and which branch should happen using code.
So if EliteScript feels like "configuration", Lua feels like "configuration plus decision-making".
What To Read First
If you are starting from almost zero, read the pages in this order:
- This page -- start with "How To Read This Page If You Are New To Programming" and "Mental Model"
- Tiny Lua Primer (below) -- just enough syntax to follow along
- Your First Working Power, Built Slowly (below) -- hands-on, one step at a time
- Copy-Paste Starter Templates (below) -- grab one and modify it
- Hook Reference (below) -- see what events you can react to
- API Reference -- learn the
contextsections you actually use - Examples & Patterns -- study and adapt real-world powers
You do not need to memorize everything before writing a first power. You only need enough to make one hook work, then you can come back for more.
Tiny Lua Primer For EliteMobs Authors
This is the minimum Lua syntax most power authors need.
Variables
Use local to store a value:
local cooldown_key = "fire_burst"
local damage_multiplier = 1.5
local means the variable belongs only to this file or block.
Functions
Functions are reusable blocks of logic:
local function warn_player(player)
player:send_message("&cMove!")
end
Later, you can call it:
warn_player(context.player)
if checks
Use if when something should only happen sometimes:
if context.player == nil then
return
end
That means "if there is no player for this hook, stop here".
nil
nil means "no value". It is Lua's version of "nothing is here".
You will often check for nil with:
if context.event ~= nil then
-- do something with the event
end
~= means "is not equal to".
Tables
Lua uses tables for several jobs:
- lists
- objects with named keys
- the final returned power definition
Example of a table with named keys:
local particle = {
particle = "FLAME",
amount = 1,
speed = 0.05
}
Returning the power definition
At the end of the file, you return one table:
return {
api_version = 1,
on_spawn = function(context)
end
}
That returned table is the power file.
Comments
Use -- to write a note for humans:
-- This cooldown stops the attack from firing every hit
context.cooldowns:set_local(60, "fire_burst")
Your First Working Power, Built Slowly
If someone is completely new, this is the best "first win" progression.
Before Step 1: Save it and attach it to a boss
Save the Lua file in the normal EliteMobs powers folder, for example:
plugins/
EliteMobs/
powers/
first_test.lua
Then add that filename to a boss config using the normal powers: list:
powers:
- first_test.lua
So the full beginner loop is:
- save the file in
plugins/EliteMobs/powers/ - add the
.luafilename to the bosspowers:list - spawn or reload that boss
- test the hook you are currently building
If you need more background on boss files, power lists, or how custom bosses are structured, see Creating Custom Bosses. That page covers boss setup in detail, including how the powers: list works.
Step 1: Make the file load
return {
api_version = 1,
on_spawn = function(context)
end
}
If this loads without errors, you already proved:
- the file is valid Lua
- EliteMobs found it
- the returned table shape is correct
on_spawnis a valid hook name
Step 2: Make the boss do one visible thing
return {
api_version = 1,
on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}
Now you have the most important beginner proof: your hook is firing.
Step 3: React to a player hit
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
context.player:send_message("&eYou hit the boss.")
end
}
This teaches three core ideas:
on_boss_damaged_by_playeris the hook namecontext.playeris the player involved in that hookreturnexits early when the data you need is missing
Step 4: Prevent spam with a cooldown
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not context.cooldowns:local_ready("hello_message") then
return
end
context.player:send_message("&eYou woke up the boss.")
context.cooldowns:set_local(60, "hello_message")
end
}
This is the first genuinely useful pattern most authors need. If you understand this pattern, you can already build many practical powers. See context.cooldowns for the full cooldown API.
Step 5: Add one real effect
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not context.cooldowns:local_ready("shock") then
return
end
context.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
context.cooldowns:set_local(100, "shock")
end
}
At this point, you are already writing a real power.
First Real Workflow For New Authors
When building a brand-new Lua power, use this order:
- Create the file and make
on_spawnwork. - Change to the actual hook you want.
- Confirm the hook has the data you expect, such as
context.playerorcontext.event. - Add a message or sound first, before damage or particles.
- Add cooldowns.
- Add one gameplay effect.
- Only after that, add helpers, state, scheduler logic, or zones.
That order makes debugging dramatically easier because only one thing changes at a time.
Where Lua Files Live
Place .lua files in the same folder tree as normal power .yml files:
plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua
Lua powers are auto-discovered from the power directories EliteMobs already loads.
How Bosses Reference Lua Powers
Boss files still use the normal powers: list:
powers:
- attack_push.yml
- mycoolpower.lua
No special field is required. Lua powers are not loaded through eliteScript:.
Important File Naming Rules
- Bosses reference Lua powers by filename, not by folder path.
- EliteMobs currently registers discovered Lua powers by basename only.
- Avoid duplicate names like
powers\fire.luaandpowers\bosses\fire.lua, because one can overwrite the other during discovery.
Premade Lua Powers
EliteMobs now seeds several premade powers as Lua files in the powers folder, including:
attack_blinding.luaattack_confusing.luaattack_fire.luaattack_freeze.luaattack_gravity.luaattack_poison.luaattack_weakness.luaattack_web.luaattack_wither.luacorpse.luainvisibility.luainvulnerability_fire.luamoonwalk.lua
For backwards compatibility, those premades are also registered under their legacy .yml names.
Minimal File Contract
Every Lua power must return a table. That table is intentionally strict.
Required and optional top-level fields
| Field | Required | Type | Notes |
|---|---|---|---|
api_version | Yes | Number | Currently must be 1 |
priority | No | Number | Execution priority. Lower values run first. Defaults to 0 |
| supported hook keys | No | Function | Must use one of the exact hook names listed below |
Validation rules
- The file must return a table.
api_versionis required and must currently be1.prioritymust be numeric if present.- Every extra top-level key must be a supported hook name.
- Every hook key must point to a function.
- Unknown top-level keys are rejected.
This means helper functions and local constants should live above the final return, not inside the returned table unless they are actual hooks.
Quick Start Example
This power strikes the attacking player with lightning and rate-limits itself with a local cooldown:
return {
api_version = 1,
priority = 0,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not context.cooldowns:local_ready("lightning_burst") then
return
end
context.player:send_message("&cThe boss crackles with energy!")
context.world:strike_lightning_at_location(context.player.current_location)
context.cooldowns:set_local(60, "lightning_burst")
end
}
Copy-Paste Starter Templates
Smallest valid Lua power
return {
api_version = 1,
on_spawn = function(context)
end
}
Recommended starter template
local ATTACK_COOLDOWN = "my_attack"
local function can_run_attack(context)
return context.cooldowns:local_ready(ATTACK_COOLDOWN)
and context.cooldowns:global_ready()
end
local function run_attack(context)
context.boss:play_sound_at_self("entity.blaze.shoot", 1.0, 1.0)
context.cooldowns:set_local(100, ATTACK_COOLDOWN)
context.cooldowns:set_global(20)
end
return {
api_version = 1,
priority = 0,
on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end
if not can_run_attack(context) then
return
end
run_attack(context)
end
}
Recommended layout for larger files
local CONSTANT_NAME = "value"
local function helper_function(context)
end
local function another_helper(context, value)
end
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
end,
on_enter_combat = function(context)
end,
on_boss_damaged_by_player = function(context)
end,
on_exit_combat = function(context)
end
}
Hook Reference
Lua powers currently support these top-level hooks. Each hook is just a specially named function in the table you return.
For details on what context keys are available in each hook, see the API Reference.
| Hook | Fires when |
|---|---|
on_spawn | The elite mob spawns |
on_game_tick | Once every tick while the runtime is active |
on_boss_damaged | The boss is damaged |
on_boss_damaged_by_player | The boss is damaged by a player |
on_boss_damaged_by_elite | The boss is damaged by another elite |
on_player_damaged_by_boss | A player is damaged by the boss |
on_enter_combat | The boss enters combat |
on_exit_combat | The boss leaves combat |
on_heal | The boss heals |
on_boss_target_changed | The boss changes target |
on_death | The boss dies |
on_phase_switch | A phase boss switches phase |
on_zone_enter | A script zone enter event fires |
on_zone_leave | A script zone leave event fires |
on_zone_enter and on_zone_leave are top-level hook names. They are separate from the callback-style on_enter and on_leave keys used by context.zones:watch_zone(...) and zone:watch(...), where you pass a small table of functions for the watcher to run later.
Method Syntax: : vs .
Most exposed helpers are written so that either of these styles works:
context.cooldowns:set_local(60, "test")
context.cooldowns.set_local(60, "test")
For readability, this page and the API Reference use : method syntax.
Beginner Progression Path
If you want to learn this system from scratch, this progression works well:
- Write a file with only
api_version = 1andon_spawn. - Make the boss send a message or play a sound.
- Add a cooldown with
context.cooldowns. - Add one player-triggered hook such as
on_boss_damaged_by_player. - Add one delayed action with
context.scheduler:run_after(...). - Add one simple native Lua zone query or one simple
context.script:target(...). - Only then move into rotating attacks, state machines, and multi-step mechanics.
Migration Advice For EliteScript Authors
If you already write good EliteScripts, the easiest way to learn Lua powers is:
- Keep thinking in terms of events, targets, zones, relative vectors, and particles.
- Move your control flow into Lua: random rolls, shared helper functions, loops, state, and task scheduling.
- Keep your geometry and targeting logic in
context.scriptso you can continue using the existing EliteScript documentation as your reference.
That approach gives you much more flexibility without throwing away the excellent existing scripting docs.
Next Steps
Now that you know the basics, explore:
- API Reference -- the full
context.*reference for every method, key, and callback - Examples & Patterns -- complete working powers you can copy, adapt, and learn from
- Enum Reference -- valid values for Particle, Sound, Material, and other string constants
