Lua 腳本:區域與目標選擇
EliteMobs 的 Lua 功能提供了兩種互補的方法來定義空間區域和解析目標:
- 原生區域 (
context.zones) -- 簡單直接。將區域定義構建為普通的 Lua 表,然後查詢實體或位置。最適合簡單的「這個區域內有東西嗎?」檢查。 - 腳本工具 (
context.script) -- 更豐富的目標選擇,帶有 watch/contains/entities 方法的區域控制碼,粒子生成、傷害和推動動作。使用與 EliteScript 區域和 EliteScript 目標相同的欄位名稱以便於熟悉。
兩種方法都是原生 Lua API。選擇適合你能力複雜度的方法。
原生區域:context.zones
原生區域讓你將區域定義為普通的 Lua 表並直接查詢。沒有控制碼,沒有額外抽象 -- 只是一個描述形狀的表和一個查詢它的方法呼叫。
方法
| 方法 | 說明 |
|---|---|
zones:get_entities_in_zone(zoneDef, options) | 傳回區域內實體包裝器的陣列 |
zones:get_locations_in_zone(zoneDef, options) | 傳回區域內位置表的陣列 |
zones:zone_contains(zoneDef, location[, "full"|"border"]) | 如果位置在區域內則傳回 true |
zones:watch_zone(zoneDef, callbacks, options) | 註冊一個每 tick 觸發的持久區域觀察器 |
區域定義欄位
| 欄位 | 類型 | 預設值 | 說明 |
|---|---|---|---|
kind(或 type) | string | — | "sphere", "dome", "cylinder", "cuboid", "cone", "static_ray", "rotating_ray", "translating_ray" |
radius | number | 0 | 區域半徑(sphere、dome、cylinder、cone) |
height | number | 0 | 圓柱體高度 |
origin | location | Boss 位置 | 中心位置 |
destination | location | Boss 位置 | 射線和錐體的終點 |
x, y, z | number | 0 | 長方體半尺寸 |
thickness / point_radius / pointRadius | number | 0.5 | 射線粗細 |
border_radius / borderRadius | number | 1 | 邊界寬度 |
x_border, y_border, z_border | number | 1 | 長方體邊界寬度 |
animation_duration | int | 0 | 動畫射線的動畫長度(tick 為單位) |
pitch_pre_rotation, yaw_pre_rotation | number | 0 | 預旋轉角度(旋轉射線) |
pitch_rotation, yaw_rotation | number | 0 | 每 tick 旋轉角度(旋轉射線) |
origin_end, destination_end | location | Boss 位置 | 平移射線的結束位置 |
ignores_solid_blocks | boolean | true | 射線是否穿過實體方塊 |
length | number | 0 | 僅在省略 destination 時讀取,詳見下文。 |
每個多字詞欄位同樣接受 camelCase 寫法(borderRadius、xBorder、animationDuration、pitchPreRotation、ignoresSolidBlocks⋯⋯),因此從 EliteScript 的 Zone: 區塊複製過來的規格大致上可以直接使用。
kind 區分大小寫與 Lua API 中幾乎所有其他字串不同,kind 是精確比對的,且必須為小寫。"SPHERE" 或 "Sphere" 不會比對到任何東西,該區域會靜默地解析為空 —— 查詢只會傳回空清單,且不會有任何主控台警告。
destinationorigin 與 destination 在省略時都會退回為 Boss 自身的位置。對於錐體或射線而言,這會產生一個長度為零的形狀,而不是報錯。請務必在 cone、static_ray、rotating_ray 與 translating_ray 上設定 destination。並沒有 length 簡寫 —— 請明確設定終點位置。
destination 或 length省略 origin 時使用 Boss 的位置。省略 destination 時,從 origin 沿著 origin 位置表中儲存的朝向延伸 length 格,得到終點。
可以透過以下兩種方式定義錐體或射線:
-- Explicit endpoint
local explicit_ray = { kind = "static_ray", origin = context.boss:get_eye_location(),
destination = context.player:get_eye_location() }
-- Or a length along the origin's own facing
local forward_ray = { kind = "static_ray", origin = context.boss:get_eye_location(), length = 15 }
使用 length 簡寫時,只有 origin 包含 yaw/pitch 才能朝向預期方向。API 回傳的位置表(get_location()、get_eye_location()、current_location)包含這些值。手寫的 { x = .., y = .., z = .. } 不包含;em.create_location(x, y, z) 也需要傳入選用的 yaw 和 pitch。
兩者都省略時,length 預設為 0,形狀長度為零,所有查詢都會回傳空結果且不發出警告。
查詢選項
| 鍵 | 說明 |
|---|---|
filter | "player" / "players", "elite" / "elites", "mob" / "mobs", "living"(預設) |
mode | "full"(預設)或 "border" |
coverage | 0.0 到 1.0 -- 取樣位置的比例(預設 1.0) |
監視回呼
| 鍵 | 說明 |
|---|---|
on_enter | function(entity) -- 當實體進入區域時呼叫 |
on_leave | function(entity) -- 當實體離開區域時呼叫 |
範例:基本球體查詢
範例
return {
api_version = 1,
on_spawn = function(context)
-- Store a zone definition for reuse
context.state.danger_zone = {
kind = "sphere",
origin = context.boss:get_location(),
radius = 10
}
end,
on_boss_damaged_by_player = function(context)
-- Update the origin to the boss's current position
context.state.danger_zone.origin = context.boss:get_location()
local players = context.zones:get_entities_in_zone(
context.state.danger_zone,
{ filter = "players" }
)
for _, player in ipairs(players) do
player:send_message("&cYou are in the danger zone!")
end
end
}
範例:帶進入/離開的區域監視
範例
return {
api_version = 1,
on_spawn = function(context)
context.zones:watch_zone(
{
kind = "sphere",
origin = context.boss:get_location(),
radius = 8
},
{
on_enter = function(entity)
entity:apply_potion_effect("SLOWNESS", 40, 1)
entity:send_message("&7You feel sluggish near the boss...")
end,
on_leave = function(entity)
entity:send_message("&aYou escape the slowing aura.")
end
},
{ filter = "players", mode = "full" }
)
end
}
觀察器注意事項:
- 觀察器回呼直接接收單個實體包裝器,而不是透過
context.event - 當 Boss 被移除時,觀察器會自動清理
- 每個觀察器每 tick 執行一次,因此保持回呼邏輯輕量
- Boss 實體本身被排除在區域查詢之外
腳本工具:context.script
腳本工具提供目標解析、區域控制碼、相對向量、粒子生成和戰鬥動作。
context.script 執行的就是 EliteScript 引擎context.script 並不只是「帶有 EliteScript 風味的命名」 —— 你傳入的規格表會被轉換成一個普通的 Java map,並直接交給驅動 YAML EliteScript 的同一批目標、區域、相對向量與粒子類別。並沒有另一套獨立的實作。
這在實務上代表:
- EliteScript 目標、EliteScript 區域與 EliteScript 相對向量所記載的每一個欄位都能在這些規格表中使用,包括本頁未列出的任何欄位。
- 列舉值是精確的 UPPER_SNAKE_CASE(
"NEARBY_PLAYERS"、"SPHERE"、"ZONE_FULL"),與 YAML 相同。 - 欄位名稱就是 YAML 的名稱,因此
Target與Target2是大寫開頭,而targetType與borderRadius則是 camelCase。 - YAML 的載入期行為同樣適用:無法解析的值會記錄一則 EliteScript 警告並清空該欄位,而不是退回其預設值。
這與 context.zones 恰好相反,後者是使用小寫 kind 名稱、真正獨立的輕量實作。
方法
| 方法 | 說明 |
|---|---|
script:target(spec) | 從目標規格表建立目標控制碼 |
script:zone(spec) | 從區域規格表建立區域控制碼 |
script:relative_vector(spec[, actionLocation][, zoneHandle]) | 建立相對向量控制碼 |
script:damage(targetHandle, amount[, multiplier]) | 對已解析的目標造成傷害 |
script:push(targetHandle, vectorOrHandle[, additive]) | 推動已解析的目標 |
script:set_facing(targetHandle, vectorOrHandle) | 設定目標的朝向方向 |
script:spawn_particles(targetHandle, particleSpec) | 在已解析的目標位置生成粒子 |
目標控制碼方法
| 方法 | 說明 |
|---|---|
handle:entities() | 傳回實體包裝器陣列 |
handle:locations() | 傳回位置表陣列 |
handle:first_entity() | 傳回第一個實體或 nil |
handle:first_location() | 傳回第一個位置或 nil |
目標規格鍵
| 鍵 | 預設值 | 說明 |
|---|---|---|
targetType | "SELF" | 任何 EliteScript 目標類型 |
range | 20 | 附近類型目標的範圍 |
coverage | 1.0 | 0.0 到 1.0。只有區域類目標類型會採納;用在其他類型上會被重設為 1.0 並發出主控台警告 |
offset | 0,0,0 | "x,y,z" 字串或 { x = n, y = n, z = n } 表 |
relativeOffset | 無 | 一個相對向量規格表,用於相對 Boss 朝向的偏移 |
location | 無 | 單一位置,供 "LOCATION" 使用 |
locations | 無 | 位置清單,供 "LOCATIONS" 使用 |
track | true | 是否重新解析移動的目標 |
全部 17 種 EliteScript 目標類型都被接受,不只是常見的那幾種:SELF、SELF_SPAWN、DIRECT_TARGET、NEARBY_PLAYERS、NEARBY_MOBS、NEARBY_ELITES、WORLD_PLAYERS、ALL_PLAYERS、LOCATION、LOCATIONS、ZONE_FULL、ZONE_BORDER、LANDING_LOCATION、ACTION_TARGET、INHERIT_SCRIPT_TARGET、INHERIT_SCRIPT_ZONE_FULL、INHERIT_SCRIPT_ZONE_BORDER。ACTION_TARGET 與 INHERIT_* 類型只有在存在外圍 EliteScript 情境時才會解析出東西,因此從 Lua 使用它們用處不大。
範例:建立和使用目標
範例
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if not context.cooldowns:check_local("roar", 200) then return end
-- Find all players within 20 blocks
local nearby = context.script:target({
targetType = "NEARBY_PLAYERS",
range = 20
})
for _, player in ipairs(nearby:entities()) do
player:send_message("&eThe boss roars in fury!")
end
-- Single-entity access
local closest = nearby:first_entity()
if closest then
closest:show_title("&cRUN!", "&7The boss is targeting you")
end
end
}
區域控制碼方法
| 方法 | 說明 |
|---|---|
handle:full_target([coverage]) | 傳回整個區域體積的目標控制碼 |
handle:border_target([coverage]) | 傳回區域邊界的目標控制碼 |
handle:full_locations([coverage]) | 傳回整個區域體積中的位置 |
handle:border_locations([coverage]) | 傳回區域邊界上的位置 |
handle:full_entities() | 傳回整個區域體積中的實體 |
handle:border_entities() | 傳回區域邊界上的實體 |
handle:contains(location[, "full"|"border"]) | 如果位置在區域內則傳回 true |
handle:watch(callbacks[, mode]) | 監視區域的進入/離開事件,傳回任務 ID |
區域規格鍵
| 鍵 | 預設值 | 說明 |
|---|---|---|
shape | "CYLINDER" | "SPHERE", "DOME", "CYLINDER", "CUBOID", "CONE", "STATIC_RAY", "ROTATING_RAY", "TRANSLATING_RAY" |
radius | 5 | 球體、圓頂、圓柱體、錐體 |
height | 1 | 僅圓柱體 |
x, y, z | 0 | 長方體半尺寸 |
xBorder, yBorder, zBorder | 0 | 長方體邊界寬度 |
borderRadius | 1 | 球體/圓頂/圓柱體/錐體的邊界寬度 |
pointRadius | 0.5 | 射線粗細 |
animationDuration | 0 | 動畫射線的動畫長度(tick 為單位) |
Target | {targetType = "SELF"} | 中心目標規格(表) -- 使用相同的目標規格格式 |
Target2 | 無 | 第二個點。錐體與所有射線形狀必填 |
FinalTarget, FinalTarget2 | 無 | 終點位置,僅平移射線適用 |
filter | "PLAYER" | "PLAYER"、"ELITE"、"LIVING" -- 注意這裡是大寫,與 context.zones 不同 |
ignoresSolidBlocks | true | 僅射線適用 |
pitchPreRotation, yawPreRotation | 0 | 僅旋轉射線適用 |
pitchRotation, yawRotation | 0 | 僅旋轉射線適用 |
不適用於所選 shape 的鍵會被讀取但不會有任何抱怨,然後被忽略 —— 在球體上設定 height 不會有任何作用。
範例:帶傷害和粒子的區域
範例
return {
api_version = 1,
on_enter_combat = function(context)
-- Create a sphere zone centered on the boss
if context.state.zone_task_id ~= nil then return end
local zone = context.script:zone({
shape = "SPHERE",
radius = 6,
Target = { targetType = "SELF" }
})
-- Spawn warning particles on the zone border
context.script:spawn_particles(
zone:border_target(0.3),
{ particle = "FLAME", amount = 1, speed = 0.02 }
)
-- Damage all players inside the zone
local targets = zone:full_target()
context.script:damage(targets, 5.0)
-- Repeat every 20 ticks
context.state.zone_task_id = context.scheduler:run_every(20, function(ctx)
local z = ctx.script:zone({
shape = "SPHERE",
radius = 6,
Target = { targetType = "SELF" }
})
ctx.script:spawn_particles(
z:border_target(0.3),
{ particle = "FLAME", amount = 1, speed = 0.02 }
)
ctx.script:damage(z:full_target(), 5.0)
end)
end,
on_exit_combat = function(context)
if context.state.zone_task_id ~= nil then
context.scheduler:cancel_task(context.state.zone_task_id)
context.state.zone_task_id = nil
end
end
}
相對向量規格鍵
| 鍵 | 說明 |
|---|---|
SourceTarget | 來源目標規格(表) |
DestinationTarget | 目的地目標規格(表) |
normalize | boolean -- 是否正規化結果向量 |
multiplier | 正規化後套用的縮放因子 |
offset | "x,y,z" 字串或 { x = n, y = n, z = n } 表 |
相對向量控制碼方法
| 方法 | 說明 |
|---|---|
handle:resolve() | 傳回計算後的向量表 |
範例:使用相對向量推動目標
範例
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
if not context.cooldowns:check_local("knockback", 100) then return end
-- Build a vector from the boss toward the attacker
local vec = context.script:relative_vector({
SourceTarget = { targetType = "SELF" },
DestinationTarget = { targetType = "DIRECT_TARGET" },
normalize = true,
multiplier = 2.5
})
-- Push the attacker away
local target = context.script:target({
targetType = "DIRECT_TARGET"
})
context.script:push(target, vec)
end
}
粒子規格格式
粒子規格可以是字串、單個表或表的陣列。由於這條路徑執行的是 EliteScript 粒子引擎,這些鍵就是記載於 SPAWN_PARTICLE 底下的 EliteScript 鍵:
| 鍵 | 預設值 | 說明 |
|---|---|---|
particle | "FLAME" | 粒子名稱(如 "FLAME"、"DUST"、"SMOKE") |
amount | 1 | 粒子數量。0 會把 x/y/z 轉為速度向量 |
x, y, z | 0.01 | 偏移/擴散值,當 amount 為 0 時則為速度 |
speed | 0.01 | 粒子速度 |
red, green, blue | 255 | DUST、DUST_COLOR_TRANSITION、WITCH 及其他顏色資料粒子的顏色(0-255) |
toRed, toGreen, toBlue | 255 | DUST_COLOR_TRANSITION 的過渡目標顏色 |
material | "STONE" | 帶有方塊/物品資料的粒子(BLOCK、ITEM、FALLING_DUST ⋯⋯)所顯示的方塊或物品 |
relativeVector | 無 | 一個相對向量規格。設定它會強制把 amount 設為 0,並以解析出的方向覆寫 x/y/z |
這些 EliteScript 鍵與 context.world:spawn_particle_at_location(loc, spec) 所接受的鍵並不相同。world 表有它自己較小的讀取器,預設值也不同(amount 為 1,x/y/z/speed 為 0),沒有 material 也沒有 relativeVector,而且它額外接受 snake_case 的 to_red / to_green / to_blue。請參閱世界與環境。
有關完整的粒子名稱列表,請參閱列舉參考。
重要注意事項
- 腳本工具控制碼繫結到建立它們的事件上下文。不要將它們儲存在
context.state中以供後續鉤子呼叫使用。 - 區域
watch()傳回一個任務 ID,可以用context.scheduler:cancel_task()取消。 - Coverage 值僅適用於基於位置的解析,不適用於實體查詢。
- 所有字串值使用與 EliteScript YAML 相同的 UPPER_SNAKE_CASE 列舉名稱(如
"SELF"、"NEARBY_PLAYERS"、"SPHERE")。
em 輔助命名空間
em 命名空間在所有 Lua 能力檔案中全域可用。它提供了位置、向量和區域定義的便捷建構函式。
位置和向量建構函式
| 函式 | 說明 |
|---|---|
em.create_location(x, y, z[, world][, yaw][, pitch]) | 傳回帶有 add(dx, dy, dz) 方法的位置表 |
em.create_vector(x, y, z) | 傳回向量表 |
區域建構器輔助函式
em.zone 子表提供建構器函式,傳回與 context.zones 相容的區域定義表。每個建構器傳回帶有可鏈式呼叫的修改器方法的表。
| 函式 | 參數 | 修改器 |
|---|---|---|
em.zone.create_sphere_zone(radius) | radius | :set_center(location) |
em.zone.create_dome_zone(radius) | radius | :set_center(location) |
em.zone.create_cylinder_zone(radius, height) | radius, height | :set_center(location) |
em.zone.create_cuboid_zone(x, y, z) | x, y, z(半尺寸) | :set_center(location) |
em.zone.create_cone_zone(length, radius) | length, radius | :set_origin(location), :set_destination(location) |
em.zone.create_static_ray_zone(length, thickness) | length, thickness | :set_origin(location), :set_destination(location) |
em.zone.create_rotating_ray_zone(length, point_radius, animation_duration) | length, point_radius, animation_duration | :set_origin(location), :set_destination(location) |
em.zone.create_translating_ray_zone(length, point_radius, animation_duration) | length, point_radius, animation_duration | :set_origin(location), :set_destination(location) |
length argument is not used錐體與射線建構器會把 length 存進規格表,但 context.zones 從不會讀取它 —— 該形狀是從 origin 延伸到 destination。請務必在這些建構器上串接 :set_origin(...) 與 :set_destination(...);否則兩者都會預設為 Boss 的位置,該形狀的長度就是零。
範例
範例
return {
api_version = 1,
on_spawn = function(context)
-- Create a location offset from the boss
local boss_loc = context.boss:get_location()
local above = em.create_location(boss_loc.x, boss_loc.y + 5, boss_loc.z)
-- Create a sphere zone using the builder
local zone = em.zone.create_sphere_zone(10):set_center(boss_loc)
-- Use with native zone queries
local players = context.zones:get_entities_in_zone(zone, { filter = "players" })
for _, p in ipairs(players) do
p:send_message("&cYou are within the boss's aura!")
end
-- Create a directional vector
local push_vec = em.create_vector(0, 1.5, 0)
context.boss:set_velocity_vector(push_vec)
end
}
em 命名空間不是按實例的 -- 所有 Lua 能力實例共享相同的 em 輔助函式。這些函式是純建構函式,不攜帶任何狀態。
原生區域 vs. 腳本工具
兩個系統使用相同的底層區域幾何。以下是每種方法的使用時機:
| 使用場景 | 建議方法 |
|---|---|
| 簡單的「這個區域內有東西嗎?」檢查 | 原生區域(context.zones) |
| 使用形狀快速查詢實體 | 原生區域 |
帶 coverage 的 NEARBY_PLAYERS、ZONE_FULL | 腳本工具(context.script) |
| 動畫區域(旋轉/平移射線) | 都可以 -- 原生區域也支援這些形狀 |
帶 watch/contains/entities 方法的區域控制碼 | 腳本工具 |
| 在區域位置生成粒子 | 腳本工具(spawn_particles) |
| 與目標選擇繫結的傷害和推動動作 | 腳本工具(damage、push) |
| 方向性效果的相對向量 | 腳本工具(relative_vector) |
| 將 Lua 控制流與豐富的目標選擇結合 | 腳本工具 |
在實務中,許多能力同時使用兩者。原生區域非常適合在冷卻守衛中進行初始的「附近有玩家嗎?」檢查,而腳本工具處理隨後的複雜攻擊邏輯。
