跳到主要内容

Lua 脚本:区域与目标选择

webapp_banner.jpg

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"
radiusnumber0区域半径(sphere、dome、cylinder、cone)
heightnumber0圆柱体高度
originlocationBoss 位置形状原点:圆柱底面、锥体顶点或射线起点
destinationlocationBoss 位置射线和锥体的终点
x, y, znumber0长方体尺寸;参见点与实体的几何判定限制
thickness / point_radius / pointRadiusnumber0.5射线采样半径和间距;必须为正数
border_radius / borderRadiusnumber1从外部形状中排除的内半径
x_border, y_border, z_bordernumber1内部长方体尺寸
animation_durationint0动画射线的动画长度(tick 为单位)
pitch_pre_rotation, yaw_pre_rotationnumber0预旋转角度(旋转射线)
pitch_rotation, yaw_rotationnumber0动画全程的旋转角度;当前动画器只应用正增量
origin_end, destination_endlocationBoss 位置平移射线的结束位置
ignores_solid_blocksbooleantrue射线是否穿过实体方块
lengthnumber0构建器会保存,但省略的 destination 会在使用此备用值前变成 Boss 位置;请明确指定终点

带下划线的数值和布尔字段也接受 camelCase(borderRadius、xBorder、animationDuration、pitchPreRotation、ignoresSolidBlocks)。位置字段 origin_end 和 destination_end 必须使用下划线名称。EliteScript 的 Target 表不能替代原生位置字段。

kind 区分大小写

kind 精确匹配,必须小写。"SPHERE" 或 "Sphere" 无法匹配形状,查询会返回空列表且不输出控制台警告。

明确指定锥体和射线的端点

省略 origin 或 destination 都会使用 Boss 的当前位置。当前的位置解析器使 length 无法替代省略的终点。请提供不同的端点:

local explicit_ray = { kind = "static_ray", origin = context.boss:get_eye_location(),
destination = context.player:get_eye_location() }

要让平移射线产生动画,请提供 origin_end、destination_end 和正数 animation_duration。省略的最终端点会停留在起始位置。零时长会创建静态的初始射线;负数时长会被拒绝。最后一个动画帧会精确结束在所请求的端点上。

所有端点及查询位置都应处于 Boss 当前的世界:原生实体查询搜索的是该世界。位置表中省略的坐标为 0;省略世界会使用 Boss 的世界,但明确指定的世界名必须已经加载。尺寸必须为有限正数。点判定、粒子采样与实体判定不能互换;共用的形状限制包括完整穹顶使用球形实体判定、圆柱要求容纳实体全高,以及长方体的不同判定范围。

查询选项​

键说明
filter"player" / "players", "elite" / "elites", "mob" / "mobs", "living"(默认)
mode"full"(默认)或 "border"
coverage0.0 到 1.0 -- 采样位置的比例(默认 1.0)

filter 不区分大小写,未知值使用 living。mobs 指非玩家的活体实体,包括精英。Boss 自身被排除,但不会按游戏模式过滤玩家。mode 不区分大小写地识别 border,其他值使用 full。Coverage 随机采样位置,不采样实体,也不限制观察器成员。射线请使用 full:其边界位置尚不支持,可能导致位置查询失败。

监视回调​

键说明
on_enterfunction(entity) -- 当实体进入区域时调用
on_leavefunction(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
  • 观察器调用自身的 on_enter / on_leave,不会调用能力顶层的 on_zone_enter / on_zone_leave 钩子
  • 当 Boss 被移除时,观察器会自动清理
  • 原生观察器只创建一次形状。中心不会跟随 Boss,编辑原始表也不会移动现有观察器。移动区域应使用新查询,或使用带追踪目标的脚本区域。
  • 每个观察器每 tick 运行一次,因此保持回调逻辑轻量
  • Boss 实体本身被排除在区域查询之外

第一次检查在一个 tick 后发生;已在区域内的实体也会触发 on_enter。取消或清理不会向剩余成员发送 on_leave。本例中的球体停留在出生位置。缓慢效果从进入时持续 40 ticks;留在区域内不会刷新,离开也不会提前移除。


脚本工具:context.script​

脚本工具提供目标解析、区域句柄、相对向量、粒子生成和战斗动作。

context.script 使用 EliteScript 引擎

规格表会转换为 Java map,传给 YAML EliteScript 使用的目标、区域、相对向量与粒子类。

这在实践中意味着:

  • 规格表使用 EliteScript 目标、EliteScript 区域和 EliteScript 相对向量的解析器,包括现有限制。字段被接受,不代表 Lua 句柄具有所需的动作生命周期或继承上下文。
  • 枚举值是精确的 UPPER_SNAKE_CASE("NEARBY_PLAYERS"、"SPHERE"、"ZONE_FULL"),与 YAML 相同。
  • 使用这里列出的标准 YAML 字段名:Target、Target2、targetType、borderRadius。内部读取器不区分大小写。
  • 无效值可能产生警告、备用值,或留下稍后导致失败的缺失字段。接受表不等于验证了整个操作。

context.zones 使用独立的表读取器和小写 kind,但共用几何类。请提供含命名字段的非空规格表:空 Lua {} 会转换成 Java 列表,所以连 script:target({}) 也会失败。请改用 script:target({targetType = "SELF"})。

方法​

方法说明
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)在已解析的目标位置生成粒子

damage、push、set_facing 和 spawn_particles 需要目标句柄,不接受实体数组,且没有返回结果。它们不应用 YAML 动作的条件、等待或过滤器。damage 以 Boss 为攻击者向 Bukkit 提交伤害值;可选倍率只用于玩家战斗路径,最终生命损失由战斗处理决定。push 立即解析目标与一个向量,在下一个 tick 将同一向量用于所有目标。默认替换速度;additive = true 则叠加到该 tick 的速度上。set_facing 立即为所有目标设置同一方向。请检查方向不为零且各分量有限。

目标句柄方法​

方法说明
handle:entities()返回实体包装器数组
handle:locations()返回位置表数组
handle:first_entity()返回第一个实体或 nil
handle:first_location()返回第一个位置或 nil

每个方法都会重新解析;first_* 不读取共用快照,也不保证返回最近的结果。DIRECT_TARGET 是事件捕获的目标,并非 Boss 当前的战斗目标;事件没有提供目标时,它不会选中任何内容。

目标规格键​

键默认值说明
targetType"SELF"任意 EliteScript 目标类型
range20附近搜索框各轴的半宽;结果不按距离排序
coverage1.00.0 到 1.0;仅用于区域目标。其他类型上小于 1.0 的值会重置为 1.0 并警告
offset0,0,0逗号分隔的 "x,y,z" 字符串;此 EliteScript 字段不接受 Lua 向量表
relativeOffset无相对向量规格;源目标与目的地目标决定方向
location无"LOCATION" 的序列化字符串 "world,x,y,z,yaw,pitch"
locations无"LOCATIONS" 的序列化位置字符串数组
tracktrue是否重新解析移动的目标

Lua 句柄路径不执行 YAML 动作的目标缓存步骤。因此,静态 Lua 目标或区域上的 track = false 不会固定位置。静态区域每次查询都会重新解析原点;动画区域在创建时缓存形状。固定范围应使用带明确坐标的 LOCATION,或带已捕获 origin 的原生区域。offset 和 relativeOffset 会移动句柄解析出的每个位置,包括 LOCATION、LOCATIONS、SELF_SPAWN、基于实体的目标以及采样的区域位置;它们不会移动 entities() 返回的实体,LANDING_LOCATION 和 INHERIT_SCRIPT_TARGET 也会忽略它们。区域句柄的 full_target() 和 border_target() 不接受偏移,因此请把偏移放在区域的 Target 和 Target2 规格中。参见共享属性。

解析器接受全部 17 个名称: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。独立 Lua 句柄没有继承动作数据或落地位置。避免使用 INHERIT_* 和 LANDING_LOCATION,它们可能在解析时失败。ACTION_TARGET 只在兼容的相对向量或动作上下文中有意义。参见继承区域的限制。

ZONE_FULL 和 ZONE_BORDER 应从区域句柄取得目标;独立的 script:target(...) 没有区域。Lua 粒子辅助方法保留默认 coverage 1.0,不会采用 YAML SPAWN_PARTICLE 动作的默认值 0.3。

示例:创建和使用目标​

示例
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 along each axis
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

-- First resolved entity; this does not promise the nearest player.
local first = nearby:first_entity()
if first then
first: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"
radius5球体、穹顶、圆柱体、锥体
height1仅圆柱;实体查询时必须容纳实体全高
x, y, z0长方体尺寸;参见点与实体的几何判定限制
xBorder, yBorder, zBorder0内部长方体尺寸
borderRadius1球体、穹顶、圆柱、锥体的内半径
pointRadius0.5射线的正数采样半径和间距
animationDuration0动画射线的动画长度(tick 为单位)
Target{targetType = "SELF"}中心目标规格(表) -- 使用相同的目标规格格式
Target2无第二个点。锥体和所有射线形状必需
FinalTarget, FinalTarget2无结束位置,仅平移射线
filter"PLAYER""PLAYER"、"ELITE"、"LIVING" —— 注意这里是大写,与 context.zones 不同
ignoresSolidBlockstrue仅射线
pitchPreRotation, yawPreRotation0仅旋转射线
pitchRotation, yawRotation0仅旋转射线

不适用于所选 shape 的键会被读取但随后忽略,且不会有任何提示 —— 在球体上写 height 不会有任何效果。

contains 检查点,不检查实体包围盒。watch 的回调格式和首次检查行为与原生观察器相同。通过 context.scheduler:cancel_task(id) 取消返回的 ID,取消不会触发离开回调。静态脚本区域可以每 tick 重新解析 SELF 来跟随 Boss;动画句柄保留其形状。borderRadius 是内半径,不是边界厚度。射线应使用完整目标。

示例:带伤害和粒子的区域​

示例
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)

-- First repeat after 20 ticks, then every 20 ticks.
context.state.zone_task_id = context.scheduler:run_repeating(20, 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
}

本例在显示粒子的同时立即伤害玩家,没有预警延迟。重复操作从 20 ticks 后开始,退出战斗时停止。默认内半径 1 使半径 6 内的边界成为厚壳,而不是薄环。

相对向量规格键​

键说明
SourceTarget源目标规格(表)
DestinationTarget目的地目标规格(表)
normalize布尔值,默认 false;是否归一化向量
multiplier归一化后乘的系数,默认 1
offset逗号分隔的 "x,y,z" 字符串,默认 "0,0,0";不是 Lua 向量表

相对向量句柄方法​

方法说明
handle:resolve()返回计算后的向量表

请提供两个目标规格。解析取各自的第一个位置,依次执行目的地减源位置、可选归一化、乘法、世界坐标轴偏移。空结果或不同世界会在应用偏移前返回零;重合点归一化可能得到非有限向量。ACTION_TARGET 使用指定的 actionLocation,默认为创建句柄时捕获的 Boss 位置,不会为每个被推动实体单独替换。如果向量目标需要区域,请将区域句柄作为第三个参数传入。

示例:使用相对向量推动目标​

示例
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")
amount1粒子数量;为 0 时方向行为取决于粒子类型
x, y, z0.01偏移/扩散值,或在 amount 为 0 时作为速度
speed0.01粒子速度
red, green, blue255DUST、DUST_COLOR_TRANSITION 及要求 Color 数据的粒子颜色(0–255);WITCH 不使用这些值
toRed, toGreen, toBlue255DUST_COLOR_TRANSITION 的过渡目标颜色
material"STONE"携带方块/物品数据的粒子(BLOCK、ITEM、FALLING_DUST……)所显示的方块或物品
relativeVector无相对向量规格。设置它会强制 amount 为 0,并用解析出的方向覆盖 x/y/z

粉尘大小固定为 1。moveToTarget 会被解析,但不会移动粒子。不支持的数据类型会被跳过并输出警告。辅助方法将区域与 LOCATION / LOCATIONS 的粒子位置沿 X、Z 轴偏移 +0.5;句柄返回的位置不含此偏移。

两套不同的粒子字段

这些 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 中以供后续钩子调用使用。
  • context.script:zone(...):watch(...) 返回可由 context.scheduler:cancel_task() 取消的任务 ID。context.zones:watch_zone(...) 不返回任何值,会随能力自动清理。
  • Coverage 值仅适用于基于位置的解析,不适用于实体查询。
  • 目标、形状及过滤器枚举使用与 EliteScript YAML 相同的 UPPER_SNAKE_CASE 名称(如 "SELF"、"NEARBY_PLAYERS"、"SPHERE")。watch/contains 模式仍为 "full" 或 "border"。

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,但当前原生位置解析器会用 Boss 位置替代省略的终点。请链式调用 :set_origin(...) 和 :set_destination(...),不要依赖 length 设置方向。平移射线还需在返回表中设置 origin_end、destination_end 和正数 animation_duration。

示例​

示例
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, boss_loc.world)

-- 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
}
信息

每个脚本实例都有独立的 Lua 环境和 em 命名空间。构造函数返回新表;位置的 add(...) 方法和区域 setter 会修改调用它们的表。


原生区域 vs. 脚本工具​

两个系统使用相同的底层区域几何。以下是每种方法的使用时机:

使用场景推荐方法
简单的"这个区域内有东西吗?"检查原生区域(context.zones)
使用形状快速查询实体原生区域
带 coverage 的 NEARBY_PLAYERS、ZONE_FULL脚本工具(context.script)
动画区域(旋转/平移射线)反复读取时使用脚本区域句柄;原生观察器也能保留动画形状
带 watch/contains/entities 方法的区域句柄脚本工具
在区域位置生成粒子脚本工具(spawn_particles)
与目标选择绑定的伤害和推动动作脚本工具(damage、push)
方向性效果的相对向量脚本工具(relative_vector)
将 Lua 控制流与丰富的目标选择结合脚本工具

一个能力中可以同时使用两种 API。根据所需目标与生命周期选择:原生查询默认选择 Boss 以外的活体实体,脚本区域默认选择玩家。


后续步骤​

  • 示例与模式 -- 可供学习和改编的完整工作能力
  • NPC 脚本 -- 用于简单接近和离开的 NPC 距离钩子
  • 枚举参考 -- Particle、Sound、Material 及其他字符串常量的有效值