Creating Powers
Two ways to write a power
File-based powers live in plugins/EliteMobs/powers, including subfolders. There are two formats:
| Format | File | Use it for |
|---|---|---|
| Lua | *.lua | Scripted behavior with boss hooks, loops and state. Most shipped powers use this format. |
| EliteScript | *.yml | The supported declarative event/action format documented below. |
Of the 71 shipped powers, 68 use Lua. Only bonus_coins.yml, custom_summon.yml and elite_script.yml remain as YAML-backed Java powers. For new scripted behavior, start with Lua Getting Started and Boss Hooks & Lifecycle.
Either format is referenced from a boss the same way, via the powers list.
Use unique filenames across subfolders. The registry uses the filename, not a folder-qualified path, so subfolders do not isolate duplicate names. When two files share a name, EliteMobs loads the one whose full path sorts first and logs Duplicate content filename for the other, unless the two files have the same contents. After editing files, run /em reload and check loading warnings.
.lua power supersedes a same-named .yml powerWhen a boss asks for attack_fire.yml, EliteMobs first looks for a successfully loaded attack_fire.lua. The same lookup applies to a .yaml request. This preserves legacy references when the Lua replacement loads. New configs should name the .lua file directly. Match the registered filename's case.
.lua files are written once, and edited files are never overwrittenEach shipped Lua power is written only when no matching file was found under powers. When an update fixes a shipped power, EliteMobs lists the checksums of earlier shipped versions. An unedited copy that matches one is moved to plugins/MagmaCore/outdated files/EliteMobs/, and the current version is written on the same boot. A file you edited never matches, so it stays as it is and does not receive upstream fixes. Give modified copies their own filenames and compare retained premade files with the version you intend to use.
on_zone_enter / on_zone_leave are not fired by Lua zonesBoth hook names are accepted, but creating a Lua zone does not dispatch them. The built-in source is an EliteScript zone listener, started when a YAML script on the same boss lists ZoneEnterEvent or ZoneLeaveEvent in Events: and defines a usable zone. These events reach all matching powers on that boss and do not identify which YAML zone triggered them. Merely declaring the hooks in a Lua-only boss does not make them run.
Do not build a Lua power around these two hooks unless you are deliberately pairing it with such a YAML zone script. Use context.zones:watch_zone(...) (whose own on_enter / on_leave callbacks are invoked directly), an EliteScript Zone block, or polling from on_game_tick instead. See Hooks & Lifecycle and Script Events.
EliteMobs Scripting system
The rest of this page covers how to Create Elite Scripts!
This is an advanced feature and requires some in-depth knowledge of EliteMobs.
Note: Elite Scripts can be added as power files or to custom boss files! If used as power files, you can add them as normal powers to bosses using the powers configuration setting of Custom Bosses, as per usual.
Adding an EliteScript
To start adding an EliteScript to a boss, add the following entry to the boss file:
eliteScript:
Note: The following is extremely case and space sensitive! Make sure your spaces, line breaks and overall formatting matches the examples!
Now that you have your script start declared, you can add your specific script. For this example, we're going to create a script called Example:
eliteScript:
Example:
EliteScripts have five sections: Events, Conditions, Zone, Actions and Cooldowns. Provide a nonempty Actions list for a working script. Events can be omitted for scripts called by other scripts or landing callbacks; without a caller, such a script does not run by itself. Conditions and Cooldowns are mappings, not lists.
Let's take a look at a simple example:
eliteScript:
Example:
Events:
- EliteMobDamagedByPlayerEvent
Actions:
- action: PUSH
Target:
targetType: SELF
vValue: 0,0.5,0
Cooldowns:
local: 60
global: 20
On a qualifying player hit, this script pushes the elite upward and starts a 60-tick local cooldown and a 20-tick global cooldown. At 20 TPS those are three seconds and one second. The global cooldown affects only powers that check it; it does not block every hook or action. Event-triggered YAML scripts check cooldowns when their local or global value is positive. See the cooldown guide below for called-script behavior.
Now that you know the general format of how scripts are structured, it's time to learn what you can do with each section!
Events
Click on the link above to learn how to use events!
Targets
Click on the link above to learn how to use targets!
Actions
Click on the link above to learn how to use actions!
Zones
Click on the link above to learn how to use zones!
Conditions
Click on the link above to learn how to use conditions!
Cooldowns
Click on the link above to learn how to use cooldowns!
Adding multiple EliteScripts
You can have multiple actions on an event, but what if you want multiple scripts on the same boss? This is as simple as creating a new script entry! Let us expand on the previous example and add another script to it:
eliteScript:
Example:
Events:
- EliteMobDamagedByPlayerEvent
Actions:
- action: PUSH
Target:
targetType: SELF
vValue: 0,0.5,0
Cooldowns:
local: 60
global: 20
Example2:
Events:
- PlayerDamagedByEliteMobEvent
Actions:
- action: SET_ON_FIRE
duration: 20
Target:
targetType: DIRECT_TARGET
Cooldowns:
local: 200
global: 60
Example2 sets the damaged player on fire for 20 ticks using DIRECT_TARGET. Its 200-tick local cooldown prevents another event-triggered run of this script for ten seconds at 20 TPS. Its 60-tick global cooldown is shared with powers that check the boss's cooldown. Other scripts can still apply fire independently.
Making standalone powers
Put standalone YAML powers in plugins/EliteMobs/powers. The eliteScript block contains the behavior; the optional metadata fields below control loading, category, display and cooldown metadata. Example:
isEnabled: true
powerType: UNIQUE
eliteScript:
Example:
Events:
- EliteMobDamagedByPlayerEvent
Actions:
- action: PUSH
Target:
targetType: SELF
vValue: 0,.3,0
Cooldowns:
local: 60
global: 20
isEnabled
Controls whether the configured power is enabled; the default is true. For custom Lua, a disabled companion metadata file prevents discovery of that Lua file. A shipped Lua power still loads with a disabled companion file but is marked disabled, as explained below.
powerType
Power type supplies category metadata for default pools and stance-ring classification. The values are:
UNIQUE: Intended for explicit assignment. It is excluded from automatically seeded shared pools, but does not prevent you from explicitly putting it in a natural-power list.DEFENSIVE/MISCELLANEOUS/OFFENSIVE: Used to seed the corresponding shared list when it is absent. Eligibility depends on the configured pools, enabled state and mob level.MAJOR_ZOMBIE,MAJOR_SKELETON,MAJOR_BLAZE,MAJOR_ENDERMAN,MAJOR_GHAST: These five count as major powers; everything else counts as minor. Natural assignment uses the entity'smajorPowerslist, not a runtime check that the type suffix matches the entity.
How powerType reaches natural elites: EliteMobPowers.yml
Natural elites roll from plugins/EliteMobs/EliteMobPowers.yml, not from powerType alone. It contains:
| Key | Meaning |
|---|---|
defensivePowers | Defensive powers natural elites can randomly roll |
offensivePowers | Offensive powers natural elites can randomly roll |
miscellaneousPowers | Miscellaneous powers natural elites can randomly roll |
majorPowers.<ENTITY_TYPE> | Major powers that entity type can randomly roll |
disabledPowers.<ENTITY_TYPE> | Powers removed from that entity type after the shared lists are applied |
Missing shared defensive, offensive, and miscellaneous lists are seeded from loaded powers of the matching type. Major-power defaults are explicitly selected per entity type. Existing lists are retained, so adding a power later does not add it to those lists: edit the appropriate list yourself. Conversely, a custom Lua power present when a missing miscellaneous list is generated can enter that default pool.
Both .lua filenames and legacy .yml names are accepted in these lists; the generated defaults are written using the .yml alias. A name that resolves to no power is skipped with a console warning naming the file and the key.
Natural power slots unlock at levels 10, 20, 30 and 40 for defensive, offensive, miscellaneous and major powers respectively. A second slot in each category unlocks at 50, 60, 70 and 80. Empty or disabled pools can yield fewer powers. Editing the lists does not change a power's major/minor classification.
effect
Sets the material used for a power's orbiting stance-ring item. MAJOR_* types use the major ring; other types use the minor ring. The value must be an item material, or a particle name that needs no extra data, which orbits as a particle instead. Any other value disables the effect and logs Disabled invalid power trail.
A power with no effect (or an empty one) simply contributes no item to the ring. That is the default for custom Lua powers without an effect in their optional YAML metadata file.
Currently, standalone YAML EliteScripts do not copy this metadata into their runtime trail. Setting effect on such a YAML power therefore does not add a stance item. Custom Lua powers do copy their companion file's effect.
| Key | Values | Default |
|---|---|---|
effect | Material | none |
powerCooldown
Sets the power metadata cooldown in ticks. YAML EliteScripts use their own Cooldowns.local and Cooldowns.global values, which replace these metadata values when the script is initialized. Lua powers must use the documented cooldown API to check and set their cooldowns.
| Key | Values | Default |
|---|---|---|
powerCooldown | Integer | 0 |
globalCooldown
Sets the global cooldown metadata in ticks. A global cooldown only blocks powers that check it; it is not an unconditional lock on every hook or action. For YAML scripts, configure the script's own Cooldowns section.
| Key | Values | Default |
|---|---|---|
globalCooldown | Integer | 0 |
Making standalone Lua powers
A standalone Lua power is a .lua file in plugins/EliteMobs/powers (subfolders included). A YAML metadata file is optional.
return {
api_version = 1,
on_player_damaged_by_boss = function(context)
if context.player == nil then
return
end
context.player:apply_potion_effect("BLINDNESS", 60, 0)
end
}
Without a same-directory, same-stem .yml metadata file, a custom Lua power gets these defaults:
powerTypeisMISCELLANEOUS.effectis unset.
For a custom my_power.lua, you can add my_power.yml beside it with isEnabled, powerType, effect, powerCooldown, and globalCooldown. For example, powerType: UNIQUE keeps it out of the default miscellaneous pool. A disabled metadata file prevents that custom Lua file from loading.
A same-stem .yml or .yaml beside a shipped premade Lua power also applies to it. Its isEnabled, effect, powerCooldown and globalCooldown values replace the built-in ones, including the defaults of keys you leave out, so an omitted effect removes the power's stance item. powerType replaces the built-in type only when set. A disabled file does not stop the shipped power from loading.
Natural elites use the explicit pools in EliteMobPowers.yml; inspect those lists when adding custom powers. Add the .lua filename to a boss's powers: list to assign it directly.
Hooks available to boss Lua powers
| Hook | Fires when |
|---|---|
on_spawn | The boss spawns |
on_game_tick | Every server tick while the runtime is active and not paused or suppressed |
on_boss_damaged | The boss takes damage from any source |
on_boss_damaged_by_player | The boss takes damage from a player |
on_reinforcement_damaged_by_player | A reinforcement takes player damage; the hook runs on its summoner |
on_boss_damaged_by_elite | The boss takes damage from another elite |
on_player_damaged_by_boss | The boss damages a player |
on_enter_combat | The boss enters combat |
on_exit_combat | The boss leaves combat |
on_heal | The boss heals |
on_boss_target_changed | The player-target event, including initial acquisition; not every target change or target loss |
on_death | The boss dies |
on_phase_switch | The boss switches phase |
on_mind_action | A native Mind requests an action from the attached powers |
on_zone_enter and on_zone_leave are also accepted. The built-in dispatcher receives them from an EliteScript zone listener on the same boss, not from Lua zone watchers. See the warning at the top of this page.
Each attached Lua power has its own runtime and state for that boss. The example above intentionally has no cooldown and runs on each dispatched matching hit. Add explicit cooldown checks and setters when needed; metadata alone does not gate Lua hooks. Damage can dispatch both the general and a more specific hook, so defining both can run two handlers. See the lifecycle guide for cancellation, removal and failure behavior.
For what you can actually call inside these hooks, see Lua Getting Started, Boss Hooks & Lifecycle and Boss Entities.
