Lua 腳本:疑難排解
本頁涵蓋撰寫或除錯 Lua 能力時可能遇到的常見問題,以及從 EliteScript 遷移的作者的遷移建議。如果你正在尋找工作範例,請參閱範例與模式。如果你剛剛起步,請參閱入門指南。
常見問題
1. 能力完全無法載入
檢查伺服器啟動時控制台的錯誤。最常見的原因是 Lua 語法錯誤(缺少 end、括號不匹配等)。還要確認檔案以 .lua 結尾並放置在正確的 powers 目錄中。
2. 鉤子從未觸發
驗證鉤子名稱是否與鉤子列表中的拼寫完全一致。常見錯誤:on_boss_hit(錯誤)vs. on_boss_damaged_by_player(正確),或 on_tick(錯誤)vs. on_game_tick(正確)。
3. context.player 為 nil
並非所有鉤子都提供玩家。on_spawn、on_game_tick、on_enter_combat 和 on_exit_combat 沒有玩家。在使用 context.player 之前始終新增 nil 守衛。
4. 逾時 / 執行預算超限
如果鉤子或回呼耗時過長,能力會被自動停用以防止延遲。
[Lua] -> Your script took 73ms in 'on_tick' (limit: 50ms) — power disabled to prevent lag.
5. 排程器回呼使用過時資料
你可能使用了外部的 context 而不是回呼參數。改為 function(tick_context) ... tick_context.boss ... end。
6. 區域查詢不傳回任何實體
對於原生區域,kind 必須小寫("sphere",不是 "SPHERE")。對於腳本工具,shape 必須大寫("CONE",不是 "cone")。
7. 粒子不顯示
確認粒子名稱是有效的 Bukkit Particle 列舉值且為 UPPER_CASE。常見錯誤:"flame"(錯誤)vs. "FLAME"(正確)。
8. 冷卻似乎不起作用
確保使用 check_local(key, duration)(一次呼叫中檢查並設定),而不是 local_ready(key) 後跟單獨的 set_local(duration, key)。
9. Boss 死後繼續執行能力
在 on_exit_combat 和/或 on_death 中新增清理邏輯以取消排程器任務。
閱讀錯誤訊息
當 Lua 能力出現問題時,控制台會列印帶有 [Lua] 前綴的友善錯誤區塊。這些訊息會準確告訴你哪個檔案、哪一行、哪個鉤子以及出了什麼問題。
[Lua] Error in 'push_zone.lua' at line 35 during 'on_boss_damaged_by_player':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Power has been disabled for this boss to prevent further errors.
| 原始 Lua 錯誤 | 控制台告訴你的 |
|---|---|
attempt to call nil | 你嘗試呼叫一個不存在的方法或函式。 |
index expected, got nil | 你嘗試存取 nil 的欄位。 |
attempt to index | 你嘗試存取 nil 或無效值的屬性。 |
bad argument | 顯示具體的參數不匹配詳情。 |
| Timeout | 你的腳本在 'hook_name' 中耗時 Xms(限制:50ms)-- 能力已停用。 |
看到控制台中的 [Lua] 錯誤時,在深入程式碼之前先閱讀完整訊息 -- 它通常直接指向修復方法。
不要假設未文件化的別名存在
Lua API 暴露了一組特定的方法名。如果你手動編寫能力或借助 AI,不要假設存在簡寫或替代名稱。以下名稱不存在,會導致錯誤:
show_temporary_boss_bar()-- 使用player:show_boss_bar(title, color, style, duration)。run_command_as_player()-- 使用player:run_command(command)。em.location(...)-- 沒有em全域變數。使用context.boss:get_location()、context.player.current_location或context.world方法。em.vector(...)-- 沒有em全域變數。使用context.vectors.get_vector_between_locations(loc1, loc2)或簡單的{x=0, y=1, z=0}表。em.zone.sphere(...)-- 沒有em全域變數。使用區域定義表如{kind = "sphere", radius = 5, origin = location}。entity:teleport_to(...)-- 使用entity:teleport_to_location(location)。entity:set_velocity(...)-- 使用entity:set_velocity_vector(vector)。entity:set_facing(...)-- 使用entity:face_direction_or_location(direction_or_location)。
如有疑問,請查看 API 參考頁面(Boss 與實體、世界與環境、區域與目標選擇)。如果沒有文件記錄,就不存在。
EliteScript 作者的遷移建議
如果你已經在編寫優秀的 EliteScript,學習 Lua 能力最簡單的方法是:
-
繼續以事件、目標、區域、相對向量和粒子的概念思考。 概念相同 -- 只是語法改變了。EliteScript 事件變成了鉤子名如
on_spawn或on_boss_damaged_by_player。目標和區域作為表傳遞給context.script,使用在 EliteScript 區域 和 EliteScript 目標 頁面中記錄的相同欄位名。 -
將控制流移到 Lua。 隨機投擲、共享輔助函式、迴圈、持久狀態(
context.state)和任務排程(context.scheduler)是 Lua 新增的純 EliteScript 無法輕鬆實現的功能。首先將一個分支或條件能力轉換為 Lua,同時保持其他一切不變。 -
使用
context.script進行目標選擇和區域幾何。 腳本工具接受與 EliteScript 相同的欄位名(targetType、shape、Target、Target2、range、offset、coverage),因此你可以繼續使用現有的 EliteScript 文件作為這些規格的參考。這讓你在獲得 Lua 邏輯層靈活性的同時,利用熟悉的模式。
初學者學習路徑
- 寫一個只有
api_version = 1和on_spawn的檔案。 - 讓 Boss 傳送訊息或播放聲音。
- 用
context.cooldowns新增冷卻。 - 新增一個玩家觸發的鉤子如
on_boss_damaged_by_player。 - 用
context.scheduler:run_after(...)新增一個延遲動作。 - 新增一個簡單的原生 Lua 區域查詢或
context.script:target(...)。 - 然後才進入旋轉攻擊、狀態機和多步驟機制。
