Saltar al contenido principal

Lua Scripting: Getting Started

webapp_banner.jpg

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:

Experimental Feature

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.

Relationship To EliteScript

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:

  1. A Lua file returns one table.
  2. That table contains one or more named hooks such as on_spawn or on_boss_damaged_by_player.
  3. EliteMobs calls those hooks when something happens.
  4. Each hook receives context.
  5. You use context to inspect the boss, player, event, cooldowns, world, zones, or EliteScript bridge.

Quick meaning of the words used on this page:

  • A hook is a function EliteMobs runs for you when a specific event happens.
  • A callback is a function you pass into another helper so that helper can run your code later.
  • A lazy key is a value such as context.player or context.event that EliteMobs only resolves when you actually access it, which is why some of them can be nil in hooks where they do not apply.

The simplest workflow is:

  1. Start with one hook.
  2. Print or send one message.
  3. Add one cooldown.
  4. Add one world effect or one boss action.
  5. 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
  • context keys
  • 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 termsIn Lua that usually means
EventsHook names such as on_spawn or on_boss_damaged_by_player
Cooldownscontext.cooldowns (see context.cooldowns for full details)
ActionsDirect method calls such as context.world:spawn_particle_at_location(...) or context.script:damage(...)
Targetscontext.script:target({...})
Zonescontext.script:zone({...}) or native context.zones helpers
Relative vectorscontext.script:relative_vector({...}) or context.vectors
Script flowYour 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:

  1. This page -- start with "How To Read This Page If You Are New To Programming" and "Mental Model"
  2. Tiny Lua Primer (below) -- just enough syntax to follow along
  3. Your First Working Power, Built Slowly (below) -- hands-on, one step at a time
  4. Copy-Paste Starter Templates (below) -- grab one and modify it
  5. Hook Reference (below) -- see what events you can react to
  6. API Reference -- learn the context sections you actually use
  7. 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:

  1. save the file in plugins/EliteMobs/powers/
  2. add the .lua filename to the boss powers: list
  3. spawn or reload that boss
  4. 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_spawn is 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_player is the hook name
  • context.player is the player involved in that hook
  • return exits 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:

  1. Create the file and make on_spawn work.
  2. Change to the actual hook you want.
  3. Confirm the hook has the data you expect, such as context.player or context.event.
  4. Add a message or sound first, before damage or particles.
  5. Add cooldowns.
  6. Add one gameplay effect.
  7. 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.lua and powers\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.lua
  • attack_confusing.lua
  • attack_fire.lua
  • attack_freeze.lua
  • attack_gravity.lua
  • attack_poison.lua
  • attack_weakness.lua
  • attack_web.lua
  • attack_wither.lua
  • corpse.lua
  • invisibility.lua
  • invulnerability_fire.lua
  • moonwalk.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

FieldRequiredTypeNotes
api_versionYesNumberCurrently must be 1
priorityNoNumberExecution priority. Lower values run first. Defaults to 0
supported hook keysNoFunctionMust use one of the exact hook names listed below

Validation rules

  • The file must return a table.
  • api_version is required and must currently be 1.
  • priority must 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
}
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
}
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.

HookFires when
on_spawnThe elite mob spawns
on_game_tickOnce every tick while the runtime is active
on_boss_damagedThe boss is damaged
on_boss_damaged_by_playerThe boss is damaged by a player
on_boss_damaged_by_eliteThe boss is damaged by another elite
on_player_damaged_by_bossA player is damaged by the boss
on_enter_combatThe boss enters combat
on_exit_combatThe boss leaves combat
on_healThe boss heals
on_boss_target_changedThe boss changes target
on_deathThe boss dies
on_phase_switchA phase boss switches phase
on_zone_enterA script zone enter event fires
on_zone_leaveA 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:

  1. Write a file with only api_version = 1 and on_spawn.
  2. Make the boss send a message or play a sound.
  3. Add a cooldown with context.cooldowns.
  4. Add one player-triggered hook such as on_boss_damaged_by_player.
  5. Add one delayed action with context.scheduler:run_after(...).
  6. Add one simple native Lua zone query or one simple context.script:target(...).
  7. 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:

  1. Keep thinking in terms of events, targets, zones, relative vectors, and particles.
  2. Move your control flow into Lua: random rolls, shared helper functions, loops, state, and task scheduling.
  3. Keep your geometry and targeting logic in context.script so 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