跳至主要内容

Lua 腳本:疑難排解

webapp_banner.jpg

本頁涵蓋撰寫或除錯 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_spawnon_game_tickon_enter_combaton_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_locationcontext.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 能力最簡單的方法是:

  1. 繼續以事件、目標、區域、相對向量和粒子的概念思考。 概念相同 -- 只是語法改變了。EliteScript 事件變成了鉤子名如 on_spawnon_boss_damaged_by_player。目標和區域作為表傳遞給 context.script,使用在 EliteScript 區域EliteScript 目標 頁面中記錄的相同欄位名。

  2. 將控制流移到 Lua。 隨機投擲、共享輔助函式、迴圈、持久狀態(context.state)和任務排程(context.scheduler)是 Lua 新增的純 EliteScript 無法輕鬆實現的功能。首先將一個分支或條件能力轉換為 Lua,同時保持其他一切不變。

  3. 使用 context.script 進行目標選擇和區域幾何。 腳本工具接受與 EliteScript 相同的欄位名(targetTypeshapeTargetTarget2rangeoffsetcoverage),因此你可以繼續使用現有的 EliteScript 文件作為這些規格的參考。這讓你在獲得 Lua 邏輯層靈活性的同時,利用熟悉的模式。


初學者學習路徑

  1. 寫一個只有 api_version = 1on_spawn 的檔案。
  2. 讓 Boss 傳送訊息或播放聲音。
  3. context.cooldowns 新增冷卻。
  4. 新增一個玩家觸發的鉤子如 on_boss_damaged_by_player
  5. context.scheduler:run_after(...) 新增一個延遲動作。
  6. 新增一個簡單的原生 Lua 區域查詢或 context.script:target(...)
  7. 然後才進入旋轉攻擊、狀態機和多步驟機制。

後續步驟

  • 入門指南 -- 檔案結構、鉤子、首個能力演練、複製貼上範本
  • 範例與模式 -- 可供學習和改編的完整工作能力