Pular para o conteúdo principal

Scripting Lua: Zonas e Alvos

webapp_banner.jpg

As funcionalidades Lua do EliteMobs oferecem duas abordagens complementares para definir áreas espaciais e resolver alvos:

  • Zonas nativas (context.zones) -- simples e diretas. Você constrói uma definição de zona como uma tabela Lua simples e a consulta para obter entidades ou localizações. Ideal para verificações diretas de "há algo nesta área?".
  • Utilitários de script (context.script) -- segmentação mais rica, handles de zona com métodos watch/contains/entities, geração de partículas, dano e ações de empurrão. Usa os mesmos nomes de campo que as Zonas do EliteScript e os Alvos do EliteScript para maior familiaridade.

Ambas as abordagens são APIs Lua nativas. Escolha a que se adequa à complexidade do seu poder.


Zonas nativas: context.zones

As zonas nativas permitem definir zonas como tabelas Lua simples e consultá-las diretamente. Sem handles, sem abstração adicional -- apenas uma tabela descrevendo uma forma e uma chamada de método para consultá-la.

Métodos

MétodoNotas
zones:get_entities_in_zone(zoneDef, options)Retorna um array de wrappers de entidade dentro da zona
zones:get_locations_in_zone(zoneDef, options)Retorna um array de tabelas de localização dentro da zona
zones:zone_contains(zoneDef, location[, "full"|"border"])Retorna true se a localização está dentro da zona
zones:watch_zone(zoneDef, callbacks, options)Registra um observador de zona persistente que dispara a cada tick

Campos de definição de zona

CampoTipoPadrãoNotas
kind (ou type)string"sphere", "dome", "cylinder", "cuboid", "cone", "static_ray", "rotating_ray", "translating_ray"
radiusnumber0Raio da zona (sphere, dome, cylinder, cone)
heightnumber0Altura do cilindro
originlocationlocalização do bossLocalização central
destinationlocationlocalização do bossPonto final para raios e cones
x, y, znumber0Semi-extensões do cuboide
thickness / point_radius / pointRadiusnumber0.5Espessura do raio
border_radius / borderRadiusnumber1Largura da borda
x_border, y_border, z_bordernumber1Larguras de borda do cuboide
animation_durationint0Duração da animação em ticks para raios animados
pitch_pre_rotation, yaw_pre_rotationnumber0Ângulos de pré-rotação (raio rotativo)
pitch_rotation, yaw_rotationnumber0Ângulos de rotação por tick (raio rotativo)
origin_end, destination_endlocationlocalização do bossPosições finais para raio em translação
ignores_solid_blocksbooleantrueSe os raios passam através de blocos sólidos
lengthnumber0Só é lido quando destination é omitido; ver abaixo.

Todos os campos de várias palavras também aceitam camelCase (borderRadius, xBorder, animationDuration, pitchPreRotation, ignoresSolidBlocks, ...), pelo que uma spec copiada de um bloco Zone: de EliteScript funciona quase sempre tal como está.

O kind diferencia maiúsculas de minúsculas

Ao contrário de praticamente todas as outras strings da API Lua, o kind é comparado exatamente e tem de estar em minúsculas. "SPHERE" ou "Sphere" não correspondem a nada, e a zona resolve-se silenciosamente para nada -- as consultas devolvem apenas uma lista vazia, sem qualquer aviso na consola.

Cones e raios precisam de um destination explícito

Tanto o origin como o destination recorrem à própria localização do boss quando são omitidos. Para um cone ou um raio, isso produz uma forma de comprimento zero em vez de um erro. Defina sempre o destination em cone, static_ray, rotating_ray e translating_ray. Não existe um atalho length -- defina uma localização de destino explícita.

Os cones e raios precisam de destination ou length

Se origin for omitido, é usada a localização do chefe. Se destination for omitido, origin é projetado length blocos na direção guardada na tabela de localização origin.

Pode definir um cone ou raio destas duas formas:

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

A forma abreviada com length só aponta na direção pretendida quando origin contém yaw/pitch. As localizações devolvidas pela API (get_location(), get_eye_location(), current_location) incluem esses valores. Uma tabela escrita à mão { x = .., y = .., z = .. } não os inclui, nem em.create_location(x, y, z) sem os argumentos opcionais yaw e pitch.

Se omitir ambos, length vale 0 por predefinição: a forma tem comprimento zero e todas as consultas devolvem resultados vazios sem aviso.

Opções de consulta

ChaveNotas
filter"player" / "players", "elite" / "elites", "mob" / "mobs", "living" (padrão)
mode"full" (padrão) ou "border"
coverage0.0 a 1.0 -- fração de localizações a amostrar (padrão 1.0)

Callbacks de observação

ChaveNotas
on_enterfunction(entity) -- chamada quando uma entidade entra na zona
on_leavefunction(entity) -- chamada quando uma entidade sai da zona

Exemplo: consulta básica de esfera

Exemplo
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
}

Exemplo: observação de zona com entrada/saída

Exemplo
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
}

Notas sobre observadores:

  • Os callbacks de observadores recebem diretamente um único wrapper de entidade, não via context.event
  • Os observadores são limpos automaticamente quando o boss é removido
  • Cada observador é executado a cada tick, portanto mantenha a lógica do callback leve
  • A entidade do boss é excluída das consultas de zona

Utilitários de script: context.script

Os utilitários de script fornecem resolução de alvos, handles de zona, vetores relativos, geração de partículas e ações de combate.

O context.script executa o motor do EliteScript

O context.script não é apenas "nomenclatura ao estilo do EliteScript" -- a tabela de especificação que você passa é convertida num mapa Java simples e entregue diretamente às mesmas classes de alvo, zona, vetor relativo e partículas que fazem funcionar os EliteScripts em YAML. Não existe uma implementação separada.

O que isso significa na prática:

  • Todos os campos documentados em Alvos do EliteScript, Zonas do EliteScript e Vetores Relativos do EliteScript funcionam nestas tabelas de especificação, incluindo qualquer campo que esta página não liste.
  • Os valores de enum são exatamente em UPPER_SNAKE_CASE ("NEARBY_PLAYERS", "SPHERE", "ZONE_FULL"), tal como no YAML.
  • Os nomes dos campos são os nomes do YAML, por isso Target e Target2 são capitalizados, enquanto targetType e borderRadius são camelCase.
  • O comportamento do YAML em tempo de carregamento também se aplica: um valor que não possa ser interpretado registra um aviso do EliteScript e limpa o campo, em vez de recorrer ao seu valor padrão.

Isto é o oposto do context.zones, que é uma implementação leve genuinamente separada e que usa nomes de kind em minúsculas.

Métodos

MétodoNotas
script:target(spec)Cria um handle de alvo a partir de uma tabela de especificação de alvo
script:zone(spec)Cria um handle de zona a partir de uma tabela de especificação de zona
script:relative_vector(spec[, actionLocation][, zoneHandle])Cria um handle de vetor relativo
script:damage(targetHandle, amount[, multiplier])Causa dano aos alvos resolvidos
script:push(targetHandle, vectorOrHandle[, additive])Empurra os alvos resolvidos
script:set_facing(targetHandle, vectorOrHandle)Define a direção de orientação dos alvos
script:spawn_particles(targetHandle, particleSpec)Gera partículas nas localizações dos alvos resolvidos

Métodos do handle de alvo

MétodoNotas
handle:entities()Retorna array de wrappers de entidade
handle:locations()Retorna array de tabelas de localização
handle:first_entity()Retorna a primeira entidade ou nil
handle:first_location()Retorna a primeira localização ou nil

Chaves de especificação de alvo

ChavePadrãoNotas
targetType"SELF"Qualquer tipo de alvo do EliteScript
range20Alcance para tipos de alvo próximos
coverage1.00.0 a 1.0. Só é respeitado nos tipos de alvo de zona; em qualquer outro tipo é reposto a 1.0 com um aviso na consola
offset0,0,0String "x,y,z" ou tabela { x = n, y = n, z = n }
relativeOffsetnenhumUma tabela de especificação de vetor relativo, para um offset relativo à orientação do boss
locationnenhumLocalização única, para "LOCATION"
locationsnenhumLista de localizações, para "LOCATIONS"
tracktrueSe deve resolver novamente alvos em movimento

Todos os 17 tipos de alvo do EliteScript são aceites, não apenas os mais comuns: 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. Os tipos ACTION_TARGET e INHERIT_* só resolvem para algo quando o contexto de EliteScript envolvente existe, pelo que são de pouca utilidade a partir do Lua.

Exemplo: criar e usar um alvo

Exemplo
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
}

Métodos do handle de zona

MétodoNotas
handle:full_target([coverage])Retorna um handle de alvo para o volume completo da zona
handle:border_target([coverage])Retorna um handle de alvo para a borda da zona
handle:full_locations([coverage])Retorna localizações no volume completo da zona
handle:border_locations([coverage])Retorna localizações na borda da zona
handle:full_entities()Retorna entidades no volume completo da zona
handle:border_entities()Retorna entidades na borda da zona
handle:contains(location[, "full"|"border"])Retorna true se a localização está dentro da zona
handle:watch(callbacks[, mode])Observa a zona para eventos de entrada/saída, retorna ID de tarefa

Chaves de especificação de zona

ChavePadrãoNotas
shape"CYLINDER""SPHERE", "DOME", "CYLINDER", "CUBOID", "CONE", "STATIC_RAY", "ROTATING_RAY", "TRANSLATING_RAY"
radius5Sphere, dome, cylinder, cone
height1Apenas cylinder
x, y, z0Semi-extensões do cuboide
xBorder, yBorder, zBorder0Larguras de borda do cuboide
borderRadius1Largura da borda para sphere / dome / cylinder / cone
pointRadius0.5Espessura do raio
animationDuration0Duração da animação em ticks para raios animados
Target{targetType = "SELF"}Especificação de alvo central (tabela) -- usa o mesmo formato de especificação de alvo
Target2nenhumSegundo ponto. Obrigatório para cones e para todas as formas de raio
FinalTarget, FinalTarget2nenhumPosições finais, apenas para raio em translação
filter"PLAYER""PLAYER", "ELITE", "LIVING" -- note que aqui são MAIÚSCULAS, ao contrário do context.zones
ignoresSolidBlockstrueApenas raios
pitchPreRotation, yawPreRotation0Apenas raio rotativo
pitchRotation, yawRotation0Apenas raio rotativo

As chaves que não se aplicam à shape escolhida são lidas sem qualquer reclamação e depois ignoradas -- um height numa esfera não faz nada.

Exemplo: zona com dano e partículas

Exemplo
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
}

Chaves de especificação de vetor relativo

ChaveNotas
SourceTargetEspecificação do alvo de origem (tabela)
DestinationTargetEspecificação do alvo de destino (tabela)
normalizeboolean -- se deve normalizar o vetor resultante
multiplierFator de escala aplicado após a normalização
offsetString "x,y,z" ou tabela { x = n, y = n, z = n }

Métodos do handle de vetor relativo

MétodoNotas
handle:resolve()Retorna a tabela do vetor calculado

Exemplo: empurrar alvos com um vetor relativo

Exemplo
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
}

Formato de especificação de partículas

As especificações de partículas podem ser uma string, uma tabela individual ou um array de tabelas. Como este caminho executa o motor de partículas do EliteScript, as chaves são as do EliteScript documentadas em SPAWN_PARTICLE:

ChavePadrãoNotas
particle"FLAME"Nome da partícula (ex. "FLAME", "DUST", "SMOKE")
amount1Número de partículas. 0 transforma x/y/z num vetor de velocidade
x, y, z0.01Valores de offset/dispersão, ou velocidade quando o amount é 0
speed0.01Velocidade das partículas
red, green, blue255Cor para DUST, DUST_COLOR_TRANSITION, WITCH e outras partículas com dados de cor (0-255)
toRed, toGreen, toBlue255Cor de destino de transição para DUST_COLOR_TRANSITION
material"STONE"Bloco ou item mostrado por partículas que transportam dados de bloco/item (BLOCK, ITEM, FALLING_DUST, ...)
relativeVectornenhumUma especificação de vetor relativo. Defini-la força o amount a 0 e sobrescreve x/y/z com a direção resolvida
Dois conjuntos de chaves de partículas diferentes

Estas chaves do EliteScript não são as mesmas que o context.world:spawn_particle_at_location(loc, spec) aceita. A tabela world tem o seu próprio leitor, mais pequeno e com valores padrão diferentes (amount 1, x/y/z/speed 0), sem material e sem relativeVector, e aceita adicionalmente to_red / to_green / to_blue em snake_case. Consulte Mundo e Ambiente.

Para a lista completa de nomes de partículas, consulte a Referência de Enums.

Avisos importantes

  • Os handles de utilitários de script estão vinculados ao contexto do evento em que foram criados. Não os armazene em context.state para uso em uma chamada de hook posterior.
  • watch() de zona retorna um ID de tarefa que pode ser cancelado com context.scheduler:cancel_task().
  • Os valores de coverage aplicam-se apenas à resolução baseada em localização, não a consultas de entidades.
  • Todos os valores de string usam os mesmos nomes de enum em UPPER_SNAKE_CASE que o YAML do EliteScript (ex. "SELF", "NEARBY_PLAYERS", "SPHERE").

Namespace auxiliar em

O namespace em está disponível globalmente em todos os arquivos de poder Lua. Fornece construtores convenientes para localizações, vetores e definições de zona.

Construtores de localização e vetor

FunçãoNotas
em.create_location(x, y, z[, world][, yaw][, pitch])Retorna uma tabela de localização com um método add(dx, dy, dz)
em.create_vector(x, y, z)Retorna uma tabela de vetor

Helpers de construtor de zonas

A sub-tabela em.zone fornece funções construtoras que retornam tabelas de definição de zona compatíveis com context.zones. Cada construtor retorna uma tabela com métodos mutadores encadeáveis.

FunçãoParâmetrosMutadores
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 (semi-extensões):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)
O argumento length não é usado

Os construtores de cone e de raio guardam o length na tabela de especificação, mas o context.zones nunca o lê -- a forma vai do origin até ao destination. Encadeie sempre :set_origin(...) e :set_destination(...) nestes construtores; caso contrário, ambos recorrem à localização do boss e a forma fica com comprimento zero.

Exemplo

Exemplo
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
}
informação

O namespace em não é por instância -- todas as instâncias de poder Lua compartilham as mesmas funções auxiliares em. As funções são construtores puros e não carregam nenhum estado.


Zonas nativas vs. Utilitários de script

Ambos os sistemas trabalham com a mesma geometria de zona subjacente. Aqui está quando usar cada um:

Caso de usoAbordagem recomendada
Verificação simples de "há algo nesta área?"Zonas nativas (context.zones)
Consulta rápida de entidades com uma formaZonas nativas
NEARBY_PLAYERS, ZONE_FULL com coverageUtilitários de script (context.script)
Zonas animadas (raios rotativos/em translação)Ambos -- as zonas nativas também suportam estas formas
Handles de zona com métodos watch/contains/entitiesUtilitários de script
Geração de partículas em localizações de zonaUtilitários de script (spawn_particles)
Ações de dano e empurrão vinculadas a alvosUtilitários de script (damage, push)
Vetores relativos para efeitos direcionaisUtilitários de script (relative_vector)
Combinar fluxo de controle Lua com segmentação avançadaUtilitários de script

Na prática, muitos poderes usam ambos. As zonas nativas são ótimas para a verificação inicial de "há jogadores por perto?" em um guard de cooldown, enquanto os utilitários de script lidam com a lógica de ataque complexa que se segue.


Próximos passos

  • Exemplos e padrões -- poderes funcionais completos que você pode estudar e adaptar
  • Scripts de NPC -- hooks de proximidade de NPC para comportamentos simples de aproximação/afastamento
  • Referência de Enums -- valores válidos para Particle, Sound, Material e outras constantes de string