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 清單
EliteMobs 自己隨附的能力就是以 Lua 撰寫的,因此這套系統確實已在實際使用中 —— 但輔助方法名稱與行為在各版本之間仍可能變動。在把自訂 Lua 能力發佈到正式環境之前,請先在測試伺服器上測試,並在更新之後重新查閱本頁。
Lua 不會取代現有的 eliteScript: YAML 系統,而且 EliteScript 並未被淘汰。
- 當你需要使用現有的動作、目標、區域、條件、冷卻時間和相對向量頁面進行宣告式 YAML 驅動腳本時,請使用 EliteScript。
- 當你需要變數、迴圈、隨機選擇、可重複使用輔助函式、每個 Boss 的持久狀態和更傳統的程式設計流程時,請使用 Lua 能力檔案。
兩者的關係比表面上看起來更緊密:腳本工具 API(context.script)會把你的規格表交給實際的 EliteScript 引擎,因此 EliteScript 頁面所記載的每一個目標、區域、相對向量與粒子欄位,都能在 Lua 中逐字使用。花在學習 EliteScript 上的時間可以完全延續下來。
什麼是 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 專家才能撰寫 EliteMobs 能力。大多數能力只會用到少數幾個概念:變數(local x = 5)、函式(function foo() end)、if 檢查(if x then ... end)、表({key = value})以及 nil(Lua 的「沒有值」)。語法很輕量 —— 沒有分號、沒有大括號,只要用 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:列表 - 生成或重新載入該 Boss
- 測試你正在構建的鉤子
如果需要更多關於 Boss 檔案、能力列表或自訂 Boss 結構的背景,請參閱建立自訂 Boss。該頁面詳細說明了 Boss 的設定方式,包含 powers: 列表如何運作。
步驟 1:讓檔案載入
return {
api_version = 1,
on_spawn = function(context)
end
}
如果載入沒有錯誤,你已經證明了:
- 檔案是有效的 Lua
- EliteMobs 找到了它
- 返回表的結構是正確的
on_spawn是有效的鉤子名稱
步驟 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
}
這是大多數作者需要的第一個真正有用的模式。如果你理解了這個模式,就已經可以構建許多實用的能力了。
步驟 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.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
context.cooldowns:set_local(100, "shock")
end
}
到這裡,你已經在編寫真正的能力了。
第一個真實工作流程
從頭構建新的 Lua 能力時,使用以下順序:
- 建立檔案並讓
on_spawn工作。 - 切換到你實際需要的鉤子。
- 確認鉤子具有你期望的資料,如
context.player或context.event。 - 先新增訊息或聲音,在傷害或粒子之前。
- 新增冷卻時間。
- 新增一個遊戲效果。
- 之後才新增輔助函式、狀態、排程器邏輯或區域。
這個順序使除錯大大簡化,因為每次只改變一件事。
Lua 檔案的位置
將 .lua 檔案放在與普通能力 .yml 檔案相同的資料夾樹中:
plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua
Lua 能力會從 EliteMobs 已經載入的能力目錄中自動發現。
Boss 如何參照 Lua 能力
Boss 檔案仍然使用普通的 powers: 列表:
powers:
- attack_push.yml
- mycoolpower.lua
不需要特殊欄位。Lua 能力不透過 eliteScript: 載入。
檔案命名規則
- Boss 透過檔名參照 Lua 能力,而非資料夾路徑。
- EliteMobs 目前僅透過基本名稱註冊發現的 Lua 能力。
- 避免重複的名稱如
powers/fire.lua和powers/bosses/fire.lua,因為一個可能在發現過程中覆蓋另一個。
預製 Lua 能力
EliteMobs 自己隨附的能力現在都以 Lua 撰寫。首次啟動時大約會有七十個被寫入 plugins/EliteMobs/powers/ —— attack_fire.lua、frost_cone.lua、meteor_shower.lua、shield_wall.lua 等等。這些是最好的參考資料:直接開啟該目錄中的任何 .lua 檔案來閱讀。
預製檔案只有在不存在時才會被寫入,因此你對它們的修改能在重新啟動後保留。刪除某個檔案即可讓 EliteMobs 重新產生原始版本。
舊版 .yml 名稱仍可解析
引用舊有 attack_fire.yml 的 Boss 設定仍可正常運作。當某個能力以 .yml(或 .yaml)名稱被查找時,EliteMobs 會先檢查是否有同基本名稱的 .lua 檔案並優先使用它,否則才退回字面名稱。換句話說,.lua 能力會取代同名的 .yml 能力 —— 遷移之後你不需要編輯 Boss 檔案,而且你可以把自己的 .lua 放在旁邊來覆寫某個隨附能力。
最小檔案約定
每個 Lua 能力必須用 return 返回一個表。該表有意設計得很嚴格。
必需和可選的頂層欄位
| 欄位 | 必需 | 類型 | 備註 |
|---|---|---|---|
api_version | 是 | Number | 目前必須為 1 |
priority | 否 | Number | 執行優先順序。較低的值先執行。預設為 0 |
| 支援的鉤子鍵 | 否 | Function | 必須使用鉤子參考中列出的確切鉤子名稱之一 |
驗證規則
- 檔案必須返回一個表。
api_version是必需的,目前必須為1。priority如果存在必須是數字。- 每個額外的頂層鍵必須是支援的鉤子名稱。
- 每個鉤子鍵必須指向一個函式。
- 未知的頂層鍵會被拒絕。
這意味著輔助函式和區域常數應該放在最終 return 的上方,而非放在返回的表內部,除非它們是實際的鉤子。
複製貼上入門範本
最小的有效 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.state 中儲存資料,並在不同的鉤子中稍後讀取。
關鍵 context API
以下是你將從 context 使用的最重要的 API:
-
context.state-- 在 Boss 生命週期內持續存在的簡單 Lua 表。用於儲存階段編號、任務 ID、旗標以及你需要在鉤子之間記住的任何內容。只有context.state持續存在 -- 所有其他上下文表在每次呼叫時重新建立。 -
context.log-- 使用log:info(msg)、log:warn(msg)和log:debug(msg)進行主控台日誌記錄。在開發過程中非常寶貴。 -
context.cooldowns-- 每個能力的本地冷卻和每個 Boss 的全域冷卻。關鍵方法是cooldowns:check_local(key, ticks),它原子地檢查並設定冷卻時間。完整的冷卻 API 請參閱鉤子與生命週期頁面。 -
context.scheduler-- 使用scheduler:run_after(ticks, callback)和scheduler:run_every(ticks, callback)的延遲和重複任務。回呼接收新的 context -- 始終使用回呼參數,而非外部context。在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) 必須使用點號;冒號會將事件表格作為第一個參數傳入。
後續步驟
現在你已經了解了基礎知識,探索 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 頁面仍然是權威參考:
