Lua 腳本:入門指南
你將學到什麼
本頁將教你編寫 EliteMobs 的第一個 Lua 能力,從空白檔案一直到可用的 Boss 技能。結束時你將理解鉤子、上下文、冷卻時間以及每個 Lua 能力檔案的一般結構。
熟悉基礎知識後,繼續閱讀以下配套頁面:
- 鉤子與生命週期 -- 鉤子名稱、觸發順序和上下文可用性
- Boss 與實體 --
context.boss、context.player、context.players和context.entitiesAPI - 世界與環境 -- 粒子、聲音、閃電、生成和
context.worldAPI - 區域與目標 -- 原生 Lua 區域、腳本工具目標和
context.zones/context.scriptAPI - 範例與模式 -- 可學習和改編的完整工作能力
- 列舉與值 -- Particle、Material、PotionEffectType 等字串常數的 Spigot Javadoc 連結
- 疑難排解 -- 常見錯誤、除錯技巧和 QC 清單
Lua 能力檔案目前是實驗性的。隨著 EliteMobs 的發展,鉤子名稱、輔助方法和行為可能會發生變化,因此在生產伺服器上使用前請仔細測試。
什麼是 Lua 能力
Lua 能力是獨立的 .lua 檔案,位於 EliteMobs 正常的 powers 目錄樹中,參照方式與普通能力檔案完全相同。
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 感覺像「設定加決策」。
EliteMobs 作者的 Lua 簡明入門
這是大多數能力作者需要的最少 Lua 語法。
變數
使用 local 儲存值:
local cooldown_key = "fire_burst"
local damage_multiplier = 1.5
local 表示變數僅屬於此檔案或程式碼區塊。
函式
函式是可重複使用的邏輯區塊:
local function warn_player(player)
player:send_message("&cMove!")
end
稍後你可以呼叫它:
warn_player(context.player)
if 檢查
當某些事情只應在某些時候發生時使用 if:
if context.player == nil then
return
end
這意味著「如果此鉤子沒有玩家,在此停止」。
nil
nil 表示「沒有值」。這是 Lua 版本的「這裡什麼都沒有」。
你會經常檢查 nil:
if context.event ~= nil then
-- 對事件做些什麼
end
~= 表示「不等於」。
表
Lua 使用表來完成多種工作:
- 列表
- 具有命名鍵的物件
- 最終返回的能力定義
具有命名鍵的表範例:
local particle = {
particle = "FLAME",
amount = 1,
speed = 0.05
}
返回能力定義
在檔案末尾,你返回一個表:
return {
api_version = 1,
on_spawn = function(context)
end
}
這個返回的表就是能力檔案。
註解
使用 -- 編寫給人看的備註:
-- 此冷卻時間防止攻擊在每次命中時觸發
context.cooldowns:set_local(60, "fire_burst")
你的第一個工作能力,逐步構建
如果你是完全的新手,這是通往「第一次成功」的最佳路徑。
步驟 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。
步驟 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 在 powers 資料夾中附帶了數十個預製 Lua 能力,如 attack_fire.lua、frost_cone.lua、meteor_shower.lua 等。這些是學習的絕佳參考 -- 只需在 plugins/EliteMobs/powers/ 目錄中開啟任何 .lua 檔案。為了向後相容,預製能力也以其舊版 .yml 名稱註冊。
最小檔案約定
每個 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 = 被擊中的 Boss
-- context.player = 擊中它的玩家
-- context.world = 生成粒子、聲音等的工具
-- context.cooldowns = 冷卻時間管理
-- context.state = 你自己的持久儲存
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-- 區域幾何、目標、傷害、粒子。請參閱區域與目標。
方法語法:: 與 .
在 Lua 中,object:method(arg) 是 object.method(object, arg) 的簡寫。EliteMobs API 接受兩種形式,因此兩者都有效:
context.cooldowns:set_local(60, "test")
context.cooldowns.set_local(60, "test") -- 相同
所有文件統一使用 :。
後續步驟
現在你已經了解了基礎知識,探索 Lua 腳本文件的其餘部分:
- 鉤子與生命週期 -- 鉤子名稱、執行順序以及每個鉤子中可用的上下文鍵
- Boss 與實體 --
context.boss、context.player、context.players、context.entities和context.event - 世界與環境 -- 粒子、聲音、閃電、生成和
context.world - 區域與目標 -- 原生 Lua 區域、腳本工具的目標/區域/粒子和相對向量
- 範例與模式 -- 帶講解的完整工作能力
- 列舉與值 -- Particle、Material、PotionEffectType 等的 Spigot Javadoc 連結
- 疑難排解 -- 常見錯誤、除錯技巧和 QC 清單
對於基於 YAML 的腳本,EliteScript 頁面仍然是權威參考:
