跳到主要内容

Lua Scripting: Troubleshooting

webapp_banner.jpg

This page covers common problems you may encounter when writing or debugging Lua powers, plus migration advice for authors coming from EliteScript. If you are looking for working examples, see Examples & Patterns. If you are just getting started, see Getting Started.


Common Issues

1. Power does not load at all

Check the server console for errors when the server starts. The most common cause is a Lua syntax error (missing end, unmatched parentheses, etc.). Also verify the file ends in .lua and is placed in the correct powers directory.

2. Hook never fires

Verify the hook name is spelled exactly as listed in the hooks list. Common mistakes: on_boss_hit (wrong) vs. on_boss_damaged_by_player (correct), or on_tick (wrong) vs. on_game_tick (correct).

3. context.player is nil

Not all hooks provide a player. on_spawn, on_game_tick, on_enter_combat, and on_exit_combat do not have a player. In on_boss_damaged (generic damage), the damager may not be a player. Always add a nil guard before using context.player.

4. "Exceeded the 50ms execution budget" warning

Your hook or callback took too long. The power is automatically disabled. Common causes: iterating over too many entities, creating too many zones per tick, or running expensive string operations in on_game_tick. Move expensive work behind a cooldown gate or reduce the work done per call.

5. Scheduler callback uses stale data

You are probably using the outer context instead of the callback parameter. Change function() ... context.boss ... end to function(tick_context) ... tick_context.boss ... end.

6. Zone query returns no entities

Double-check the zone definition. For native zones, ensure kind is lowercase ("sphere", not "SPHERE"). For script utilities, ensure shape is uppercase ("CONE", not "cone"). Also verify that origin or Target actually resolves to a valid location.

7. Particles do not appear

Verify the particle name is a valid Bukkit Particle enum value in UPPER_CASE. Common mistake: "flame" (wrong) vs. "FLAME" (correct). Also check that amount is at least 1 and the location is in a loaded chunk.

8. Cooldown does not seem to work

Make sure you are using check_local(key, duration) (which checks AND sets in one call), not local_ready(key) followed by a separate set_local(duration, key). If you use local_ready alone, you only check but never set the cooldown.

9. Boss keeps running power after death

Add cleanup logic in on_exit_combat and/or on_death to cancel scheduler tasks. If the boss dies, on_exit_combat should fire, but adding explicit cleanup in both hooks is safer.


Do Not Assume Undocumented Aliases Exist

The Lua API exposes a specific set of method names. If you are writing powers by hand or with AI assistance, do not assume shorthand or alternative names exist. The following are examples of names that do not exist and will cause errors:

  • show_temporary_boss_bar() -- use player:show_boss_bar(title, color, style, duration) instead.
  • run_command_as_player() -- use player:run_command(command) instead.
  • em.location(...) -- there is no em global. Use context.boss:get_location(), context.player.current_location, or context.world methods.
  • em.vector(...) -- there is no em global. Use context.vectors.get_vector_between_locations(loc1, loc2) or plain {x=0, y=1, z=0} tables.
  • em.zone.sphere(...) -- there is no em global. Use a zone definition table like {kind = "sphere", radius = 5, origin = location}.
  • entity:teleport_to(...) -- use entity:teleport_to_location(location).
  • entity:set_velocity(...) -- use entity:set_velocity_vector(vector).
  • entity:set_facing(...) -- use entity:face_direction_or_location(direction_or_location).

When in doubt, check the API Reference pages (Boss & Entities, World & Environment, Zones & Targeting). If it is not documented there, it does not exist.


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. The concepts are the same -- only the syntax changes. EliteScript events become hook names like on_spawn or on_boss_damaged_by_player. Targets and zones are passed as tables to context.script using the same field names documented in the EliteScript Zones and EliteScript Targets pages.

  2. Move your control flow into Lua. Random rolls, shared helper functions, loops, persistent state (context.state), and task scheduling (context.scheduler) are the things Lua adds that pure EliteScript cannot do easily. Start by converting one branching or conditional power to Lua while keeping everything else the same.

  3. Use context.script for targeting and zone geometry. The script utilities accept the same field names as EliteScript (targetType, shape, Target, Target2, range, offset, coverage), so you can continue using the existing EliteScript documentation as your reference for those specs. This lets you leverage familiar patterns while gaining Lua's flexibility for the logic layer.


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.

Each step builds on the previous one, and you can test at every stage. Do not try to write a multi-phase boss as your first Lua power.


Next Steps