Lua 腳本:入門指南
你將學到什麼
本頁將教你編寫 EliteMobs 的第一個 Lua 能力,從空白檔案一直到可用的 Boss 技能。結束時你將理解鉤子、上下文、冷卻時間以及每個 Lua 能力檔案的一般結構。
熟悉基礎知識後,繼續閱讀以下配套頁面:
- 鉤子與生命週期 -- 鉤子名稱、觸發順序和上下文可用性
- NPC 腳本 -- NPC 生命週期、互動與鄰近鉤子
- Boss 與實體 --
context.boss、context.player、context.players和context.entitiesAPI - 世界與環境 -- 粒子、聲音、閃電、生成和
context.worldAPI - 區域與目標 -- 原生 Lua 區域、腳本工具目標和
context.zones/context.scriptAPI - 範例與模式 -- 可學習和改編的完整工作能力
- 列舉與值 -- Particle、Material、PotionEffectType 等字串常數的 Spigot Javadoc 連結
- 疑難排解 -- 常見錯誤、除錯技巧和 QC 清單
- 行為程式 -- 以 Lua 撰寫、透過 Boss 的
behavior設定選用的生物 AI
EliteMobs 的大多數內建能力使用 Lua,但輔助方法的名稱與行為可能隨版本變動。自訂能力投入正式環境前,請先在測試伺服器上檢查,更新後也應重新查閱參考文件。
Lua 不會取代現有的 eliteScript: YAML 系統,而且 EliteScript 並未被淘汰。
- 當你需要使用現有的動作、目標、區域、條件、冷卻時間和相對向量頁面進行宣告式 YAML 驅動腳本時,請使用 EliteScript。
- 當你需要變數、迴圈、隨機選擇、可重複使用輔助函式、每個 Boss 的持久狀態和更傳統的程式設計流程時,請使用 Lua 能力檔案。
腳本工具 API(context.script)將規格表傳給 EliteScript 的目標、區域、相對向量與粒子類別。其解析器與限制仍然適用,但獨立的 Lua 控制物件並不具備 YAML 動作的所有上下文。繼承目標和落地位置需要這些物件缺少的上下文;Lua 輔助方法也不會自動套用 YAML 動作的等待、條件或目標快取。區域與目標頁面說明了這些差異。
什麼是 Lua 能力
Lua 能力是獨立的 .lua 檔案,位於 EliteMobs 正常的 powers 目錄樹中,參照方式與普通能力檔案完全相同。
本頁教的是 Boss Lua 能力。NPC Lua 腳本是另一項獨立功能:它們位於 plugins/EliteMobs/npc_scripts/,透過 NPC 設定的 scripts: 清單附加,並使用 NPC 專屬的鉤子,例如 on_npc_proximity_enter。請參閱 NPC 腳本。
Lua 能力的優勢
Lua 能力在以下情況下表現出色:
- 攻擊輪換和隨機技能選擇
- 使用
context.state在鉤子間保持持久狀態 - 無需使用 YAML 等待構建一切的延遲和重複動作
- 在單個檔案內共享自訂輔助函式
- 純 EliteScript 中難以處理的複雜分支
- 仍然想重複使用 EliteScript 風格目標和區域定義的 Boss 邏輯
如果你的能力主要是「觸發事件,執行幾個腳本動作」,現有的 EliteScript 文件仍然是最快最清晰的構建方式。如果你的能力需要真正的程式流程,Lua 就是合適的工具。
本頁面面向的讀者
本頁面為三類讀者編寫:
- 已經了解 EliteScript,想學習 Lua 但不想一次學會「真正的程式設計」的人
- EliteMobs 腳本新手,需要包含精確名稱的完整參考的人
- 使用 AI 起草能力,需要足夠的細節來判斷 AI 何時編造了虛假資訊的人
在編寫有用的能力之前,你不需要成為完整的 Lua 開發者。對於大多數實用的 EliteMobs 能力,你真正需要的只是:
- 如何在返回的表中放置有效的鉤子
- 如何從
context讀取值 - 如何使用
if ... then return end提前退出 - 如何正確呼叫幾個輔助方法
- 如何從現有 EliteScript 文件中複製正確的目標和區域規範
心智模型:EliteScript vs. Lua
如果你了解 EliteScript,這個對比是理解 Lua 能力的最快方式:
| 如果你用 EliteScript 術語思考 | 在 Lua 中通常意味著 |
|---|---|
| Events | 鉤子名稱如 on_spawn 或 on_boss_damaged_by_player |
| Cooldowns | context.cooldowns(見下方的 context.cooldowns) |
| Actions | 直接方法呼叫如 context.world:spawn_particle_at_location(...) 或 context.script:damage(...) |
| Targets | context.script:target({...}) -- 使用 EliteScript Target 欄位名 |
| Zones | context.script:zone({...}) 或原生 context.zones 輔助 -- 見區域與目標 |
| Relative vectors | context.script:relative_vector({...}) 或 context.vectors |
| Script flow | 你自己的 Lua if 陳述式、輔助函式、計時器和狀態 |
最大的區別是:
- EliteScript 在 YAML 中描述應該發生什麼。
- Lua 讓你用程式碼決定何時、為什麼以及哪個分支應該執行。
如果 EliteScript 感覺像「設定」,那麼 Lua 感覺像「設定加決策」。
Lua 簡明入門
多數能力使用幾個 Lua 概念:變數(local x = 5)、函式(function foo() end)、if 檢查(if x then ... end)、表({key = value})和 nil(沒有值)。分號可省略。表使用大括號,函式和控制流程區塊使用 end 結束。
若需要附帶範例的完整說明,請參閱 MagmaCore Lua 腳本引擎 — Tiny Lua Primer。該入門說明由所有 Nightbreak 外掛共用,學一次就能到處適用。
你的第一個工作能力,逐步構建
如果你是完全的新手,這是通往「第一次成功」的最佳路徑。
步驟 1 之前:儲存並附加到 Boss
將 Lua 檔案儲存在 EliteMobs 的正常能力資料夾中,例如:
plugins/
EliteMobs/
powers/
first_test.lua
然後使用正常的 powers: 列表將該檔名新增到 Boss 設定中:
powers:
- first_test.lua
完整的新手流程是:
- 將檔案儲存到
plugins/EliteMobs/powers/ - 將
.lua檔名新增到 Boss 的powers:列表 - 執行
/em reload載入修改的檔案,再生成一個新的測試 Boss。重新載入會影響外掛及其運作中的內容,因此請使用測試伺服器。 - 觸發鉤子,檢查可見結果和主控台。只儲存檔案,或未重新載入就生成另一個 Boss,都不會更新已載入的定義。
如果需要更多關於 Boss 檔案、能力列表或自訂 Boss 結構的背景,請參閱建立自訂 Boss。該頁面詳細說明了 Boss 的設定方式,包含 powers: 列表如何運作。
步驟 1:讓檔案載入
return {
api_version = 1,
on_spawn = function(context)
end
}
載入器會驗證 Lua 語法、回傳表和鉤子名稱。空鉤子沒有可見結果,因此沒有錯誤並不能證明檔案已被找到或附加。加入步驟 2 的聲音並生成新 Boss,驗證整個過程。
步驟 2:讓 Boss 做一件可見的事
return {
api_version = 1,
on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}
現在你有了最重要的新手證明:你的鉤子正在觸發。
步驟 3:回應玩家的攻擊
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
}
這教授三個核心概念:
on_boss_damaged_by_player是鉤子名稱context.player是參與該鉤子的玩家return在缺少所需資料時提前退出
步驟 4:使用冷卻時間防止洗版
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
}
本地冷卻的每刻代表實際經過的 50 毫秒:即使伺服器 TPS 下降,60 也始終是三秒。這個具名冷卻由同一 Boss 的能力共用,不會在不同 Boss 之間共用。組合自訂能力時,請替鍵加上能力名稱前綴。本例只檢查自己的本地鍵,不檢查 Boss 的全域冷卻。
步驟 5:新增一個真實效果
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.cooldowns:set_local(100, "shock")
context.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
end
}
這會在世界中產生真實閃電,不是純視覺閃光。先設定冷卻再執行效果,讓效果引發的事件也能看到該冷卻。這個 100 刻的本地冷卻代表實際經過的五秒。
第一個真實工作流程
從頭構建新的 Lua 能力時,使用以下順序:
- 建立檔案並讓
on_spawn工作。 - 切換到你實際需要的鉤子。
- 確認鉤子具有你期望的資料,如
context.player或context.event。 - 先新增訊息或聲音,在傷害或粒子之前。
- 新增冷卻時間。
- 新增一個遊戲效果。
- 之後才新增輔助函式、狀態、排程器邏輯或區域。
這個順序使除錯大大簡化,因為每次只改變一件事。
Lua 檔案的位置
將 .lua 檔案放在與普通能力 .yml 檔案相同的資料夾樹中:
plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua
Lua 能力會從 EliteMobs 已經載入的能力目錄中自動發現。
可選的 YAML 中繼資料
自訂 mycoolpower.lua 可以在同一目錄旁搭配已載入的 mycoolpower.yml 或 mycoolpower.yaml。探索過程會讀取該 YAML 的 isEnabled、effect、powerType、powerCooldown 和 globalCooldown;isEnabled: false 會阻止該自訂 Lua 檔案註冊。沒有對應中繼資料時,不使用效果,能力類型為 MISCELLANEOUS。Lua 鉤子仍自行控制攻擊時機,也必須自行檢查冷卻。
內建 Lua 能力旁已載入的同主檔名 YAML 檔案,會對該能力套用相同的欄位。沒有這類檔案時,內建能力會保留隨附的中繼資料。欄位說明見建立能力。
Boss 如何參照 Lua 能力
Boss 檔案仍然使用普通的 powers: 列表:
powers:
- attack_push.yml
- mycoolpower.lua
不需要特殊欄位。Lua 能力不透過 eliteScript: 載入。
檔案命名規則
- Boss 透過檔名參照 Lua 能力,而非資料夾路徑。
- EliteMobs 目前僅透過基本名稱註冊發現的 Lua 能力。
- 避免重複的名稱如
powers/fire.lua和powers/bosses/fire.lua。EliteMobs 只會載入完整路徑依字母順序排在前面的檔案,並對被略過的副本記錄警告,除非兩個檔案完全相同。
預製 Lua 能力
大多數內建能力使用 Lua。目前原始碼定義了 68 個具體的預製 Lua 能力,檔案不存在時會安裝到 plugins/EliteMobs/powers/,包括 attack_fire.lua、frost_cone.lua、meteor_shower.lua 和 shield_wall.lua。可閱讀這些檔案作為範例,但編輯過的本機副本可能不同於外掛隨附版本。
預製檔案只在不存在時寫入,所以修改會在重新啟動後保留。當更新變更了某個內建能力時,先前隨附版本的未編輯副本會在啟動時被移至 plugins/MagmaCore/outdated files/EliteMobs/,並在原處寫入目前版本;編輯過的副本則會保留。刪除內建檔案後,下次外掛初始化會重新產生隨附版本;取代前請保留仍需要的自訂修改。
共用原始碼片段
Boss Lua 能力可以用獨占一行的註解指令引入輔助檔案:
-- @include helpers/common.inc
載入器在驗證能力前插入參照檔案的文字。路徑相對於包含指令的檔案,而且必須留在最外層能力檔案的目錄內,解析符號連結後也不例外。循環引入、超過 16 層的巢狀結構、大於 4 MiB 的檔案,以及超過 4,194,304 字元的展開原始碼都會被拒絕。
輔助片段請使用 .inc 等非 .lua 副檔名,避免探索過程也將其當作獨立能力載入。在能力最後的 return 表之前宣告輔助函式。這項指令只適用於 Boss 能力載入,不會啟用 Lua require 或檔案系統存取,NPC 腳本也不會處理它。
舊版 .yml 名稱仍可解析
參照 attack_fire.yml 的 Boss 設定仍然有效。對於 .yml 或 .yaml 參照,EliteMobs 先尋找相同基本名稱的已載入 Lua 能力,再退回原名稱。驗證失敗或停用的檔案不算已載入的替代能力。請保持基本名稱的拼字和大小寫一致。若要自訂內建 Lua 能力,請編輯現有檔案並重新載入;在其他目錄建立重複名稱不是可靠的覆寫方式。
最小檔案約定
每個 Lua 能力必須用 return 返回一個表。該表有意設計得很嚴格。
必需和可選的頂層欄位
| 欄位 | 必需 | 類型 | 備註 |
|---|---|---|---|
api_version | 是 | Number | 使用整數 1 |
priority | 否 | Number | 使用整數。較小的值先執行,預設為 0。YAML 能力的優先順序為 0 |
| 支援的鉤子鍵 | 否 | Function | 必須使用鉤子參考中列出的確切鉤子名稱之一 |
驗證規則
- 檔案必須返回一個表。
api_version是必需的,目前必須為1。priority如果存在必須是數字。- 每個額外的頂層鍵必須是支援的鉤子名稱。
- 每個鉤子鍵必須指向一個函式。
- 未知的頂層鍵會被拒絕。
這意味著輔助函式和區域常數應該放在最終 return 的上方,而非放在返回的表內部,除非它們是實際的鉤子。
檔案主體在驗證時執行一次,之後在每個 Boss 能力執行個體的獨立 Lua 環境中再次執行。context 只存在於鉤子或回呼參數中,不是頂層全域變數。區域變數可以在該執行個體中保留,但驗證時的值不會傳入其中。執行環境移除了檔案、作業系統、Java 存取和 require;原始碼片段請使用引入指令。未捕捉的鉤子或回呼錯誤會關閉該執行個體並取消其工作,下次受到攻擊時不會重試。修正檔案並重新載入後再試。
複製貼上入門範本
最小的有效 Lua 能力
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
}
鉤子
鉤子是你返回的表中具有特殊名稱的函式。EliteMobs 在發生某些事情時呼叫它們 -- Boss 生成、受到傷害、進入戰鬥等。你已經在上面的教學中看到了 on_spawn 和 on_boss_damaged_by_player。
最常用的入門鉤子是 on_spawn、on_boss_damaged_by_player、on_enter_combat 和 on_exit_combat。所有鉤子的完整列表、每個鉤子中可用的上下文鍵以及執行順序的工作方式,請參閱鉤子與生命週期。
什麼是 context?
每個鉤子函式都接收一個名為 context 的參數。把它想像成 EliteMobs 每次發生事情時遞給你的工具箱 -- 它包含了你與 Boss、玩家、世界、冷卻時間等互動所需的一切。
on_boss_damaged_by_player = function(context)
-- context.boss = the boss that was hit
-- context.player = the player who hit it
-- context.world = tools for spawning particles, sounds, etc.
-- context.cooldowns = cooldown management
-- context.state = your own persistent storage
end
你不需要自己建立 context -- EliteMobs 建立它並傳遞給你的鉤子。你只需從中讀取並呼叫方法。
每個鉤子接收新的 context。其 context.state 表屬於此 Boss 上此能力的執行個體,可為之後的鉤子和回呼保存資料。其他能力擁有獨立狀態;關閉或重新載入執行個體會捨棄狀態。
關鍵 context API
以下是你將從 context 使用的最重要的 API:
-
context.state-- 為此 Boss 上此能力的執行個體保留的一般 Lua 表。用它在鉤子間保存階段編號、工作 ID 和旗標。執行個體移除或重新載入後不保留。 -
context.log-- 使用log:info(msg)、log:warn(msg)和log:debug(msg)記錄主控台日誌。需要區分多個能力的輸出時,請在訊息中加入能力名稱。 -
context.cooldowns-- Boss 的各能力共用具名冷卻和全域冷卻。明確指定的鍵共用 Boss 範圍的命名空間,因此請加上能力名稱前綴。nil鍵選擇目前 Lua 檔名的預設冷卻。cooldowns:check_local(key, ticks)在一次呼叫中檢查並設定本地冷卻,不會檢查或設定全域冷卻。完整 API 見鉤子與生命週期。 -
context.scheduler--scheduler:run_after(ticks, callback)延遲一次回呼。scheduler:run_every(ticks, callback)重複執行,首次不等待一個間隔;需要明確的首次延遲時使用scheduler:run_repeating(delay, interval, callback)。回呼接收不含觸發事件或玩家的新上下文。使用這個回呼參數,並主動保存所需的玩家 UUID 或位置;不要假設延遲後仍有ctx.player。僅限戰鬥的重複工作應在on_exit_combat中取消,否則所屬工作會持續到取消或執行個體關閉。見鉤子與生命週期。 -
context.boss/context.player-- 參與當前事件的 Boss 和玩家。所有欄位和方法請參閱 Boss 與實體。 -
context.world-- 生成粒子、實體、聲音、閃電、方塊。請參閱世界與環境。 -
context.zones/context.script-- 區域幾何、目標、傷害、粒子。請參閱區域與目標。
方法語法:: 與 .
關於 : 與 .,請參閱 MagmaCore Lua 腳本引擎。實體方法和共用服務接受兩種形式,但 context.event.set_damage_amount(value) 與 context.event.multiply_damage_amount(multiplier) 必須使用點號;冒號會將事件表格作為第一個參數傳入。context.event:transfer_damage(target, amount) 接受冒號。em.create_location(...) 等全域建構函式使用點號。
後續步驟
現在你已經了解了基礎知識,探索 Lua 腳本文件的其餘部分:
- 鉤子與生命週期 -- 鉤子名稱、執行順序以及每個鉤子中可用的上下文鍵
- NPC 腳本 -- NPC 檔案位置、鉤子、鄰近事件與
context.npc - Boss 與實體 --
context.boss、context.player、context.players、context.entities和context.event - 世界與環境 -- 粒子、聲音、閃電、生成和
context.world - 區域與目標 -- 原生 Lua 區域、腳本工具的目標/區域/粒子和相對向量
- 範例與模式 -- 帶講解的完整工作能力
- 列舉與值 -- Particle、Material、PotionEffectType 等的 Spigot Javadoc 連結
- 疑難排解 -- 常見錯誤、除錯技巧和 QC 清單
對於基於 YAML 的腳本,EliteScript 頁面仍然是權威參考:
