Zum Hauptinhalt springen

Lua-Scripting: NPC-Scripts

webapp_banner.jpg

EliteMobs-NPC-Lua-Scripts sind eigenständige .lua-Dateien, die an NPC-Konfigurationen angehängt werden. Sie sind von den Boss-Lua-Powers getrennt: Boss-Powers liegen in plugins/EliteMobs/powers/, während NPC-Scripts in plugins/EliteMobs/npc_scripts/ liegen.

NPC-Scripts laufen jetzt auf derselben vereinheitlichten MagmaCore-Scripting-Laufzeitumgebung wie Boss-Powers, FreeMinecraftModels-Props und FMM-Items. Das bedeutet, dass ein NPC-Script die vollständige gemeinsame Scripting-Oberfläche erhält — context.world (einschließlich strike_lightning), context.zones, context.scheduler, context.cooldowns, context.log, context.event und context.playerplus eine NPC-spezifische context.npc-Tabelle. Alles, was MagmaCore für Scripts bereitstellt, ist auch hier verfügbar.

Experimentelles Feature

NPC-Lua-Scripts sind noch experimentell. Die NPC-spezifischen Hooks und die context.npc-Helfer können sich ändern. Die gemeinsamen Tabellen (context.world, context.zones, context.scheduler, context.cooldowns, context.log, context.event, context.player) sind dieselben, die in der Scripting-Engine und der Lua-API-Referenz dokumentiert sind.


Dateispeicherort

Erstellen Sie NPC-Script-Dateien in:

plugins/
EliteMobs/
npc_scripts/
wave.lua

Unterordner werden rekursiv durchsucht. Scripts werden jedoch nur anhand des Dateinamens registriert, sodass npc_scripts/wave.lua und npc_scripts/town/wave.lua kollidieren -- halten Sie die Basisnamen im gesamten Verzeichnisbaum eindeutig.

Die Endung .lua ist in einer NPC-Konfiguration optional: - wave und - wave.lua werden beide zu wave.lua aufgelöst. Verweist eine NPC-Konfiguration auf ein Script, das nicht existiert, protokolliert EliteMobs eine Warnung und der NPC spawnt trotzdem.


Scripts an NPCs anhängen

Fügen Sie der NPC-Konfiguration eine scripts:-Liste hinzu:

scripts:
- wave.lua

An einen NPC können mehrere Scripts angehängt werden:

scripts:
- wave.lua
- greeting_particles.lua

Scripts laufen in Prioritätsreihenfolge. Niedrigere priority-Werte laufen zuerst. Wird die Priorität weggelassen, ist der Standardwert 0.


Script-Aufbau

Jedes NPC-Script muss genau eine Tabelle zurückgeben:

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
context.state.spawned = true
context.npc:play_model_animation("idle")
end
}

Nur diese Top-Level-Felder werden akzeptiert:

FeldTypHinweise
api_versionnumberErforderlich. Muss 1 sein.
prioritynumberOptional. Niedrigere Werte laufen zuerst.
on_spawnfunctionLäuft, nachdem der NPC gespawnt ist.
on_removefunctionLäuft, wenn der NPC entfernt wird.
on_game_tickfunctionLäuft in jedem Server-Tick, solange der NPC gültig ist. Halten Sie dies sehr schlank.
on_npc_interactfunctionLäuft, wenn ein Spieler mit dem NPC interagiert.
on_npc_proximity_enterfunctionLäuft einmal, wenn ein Spieler den Aktivierungsradius dieses NPCs betritt.
on_npc_proximity_leavefunctionLäuft einmal, wenn ein Spieler den Aktivierungsradius dieses NPCs verlässt.
on_zone_enterfunctionLäuft, wenn ein Spieler eine Zone betritt, die dieses Script überwacht (siehe context.zones).
on_zone_leavefunctionLäuft, wenn ein Spieler eine überwachte Zone verlässt.

Die Zonenüberwachung verfolgt nur Spieler -- Mobs und andere Entities lösen on_zone_enter / on_zone_leave niemals aus.

Unbekannte Top-Level-Schlüssel werden beim Laden des Scripts abgelehnt. Hilfsfunktionen sollten als local-Funktionen oberhalb der zurückgegebenen Tabelle deklariert werden.


Näherungs-Hooks

Die NPC-Näherungs-Hooks verwenden den Konfigurationswert activationRadius des NPCs.

HookWird ausgelöst, wenn
on_npc_proximity_enterEin Spieler sich von außerhalb des Aktivierungsradius des NPCs nach innen bewegt.
on_npc_proximity_leaveEin Spieler sich von innerhalb des Aktivierungsradius des NPCs nach außen bewegt.

Diese Hooks werden vom serverseitigen Näherungs-Scanner pro NPC und pro Spieler verfolgt. In der Nähe eines NPCs zu stehen hindert einen anderen NPC nicht daran, sein eigenes Enter-Event auszulösen, und der Verbleib innerhalb des Radius löst keine wiederholten Enter-Events aus.

Das normale Verhalten für Begrüßung, Dialog und Quest-Indikator läuft weiterhin. Der Lua-Hook fügt zusätzliches Verhalten hinzu.


Gemeinsame Scripting-Oberfläche

Da NPC-Scripts auf der vereinheitlichten Laufzeitumgebung laufen, erhält jeder NPC-Hook auch die gemeinsamen MagmaCore-Context-Tabellen, die von FreeMinecraftModels-Scripts verwendet werden. EliteMobs-Boss-Powers laufen auf derselben Laufzeitumgebung, verwenden aber für mehrere Tabellen boss-spezifische Varianten. Vollständige Methodenlisten finden Sie in der Lua-API-Referenz und der Scripting-Engine:

TabelleWofür sie da ist
context.worldWelteffekte und -abfragen: strike_lightning, spawn_particle, play_sound, set_block_at, place_temporary_block, spawn_entity, spawn_firework, get_nearby_entities, get_nearby_players, raycast und mehr. Sowohl die Koordinatenform (strike_lightning(x, y, z)) als auch die Location-Tabellen-Form (strike_lightning_at_location(loc)) werden akzeptiert.
context.zonesErstellt räumliche Zonen (create_sphere(x, y, z, radius), create_cylinder(x, y, z, radius, height), create_cuboid(x, y, z, xSize, ySize, zSize)) -- jede gibt einen numerischen Handle zurück. watch(handle, on_enter, on_leave) startet die Verfolgung (die Callbacks lösen Ihre on_zone_enter / on_zone_leave-Hooks aus, nicht die übergebenen Funktionen); unwatch(handle) beendet sie.
context.schedulerrun_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id).
context.cooldownsGemeinsame MagmaCore-Abklingzeiten: local_ready, local_remaining, check_local, set_local, global_ready, set_global.
context.loginfo(msg), warn(msg), error(msg) — schreibt in die Serverkonsole.
context.eventDas aktuelle Bukkit-Event, sofern vorhanden. Siehe unten.
context.playerDer interagierende/auslösende Spieler, sofern vorhanden. Siehe unten.
context.stateEine einfache Lua-Tabelle, die für diese NPC-Script-Instanz bestehen bleibt, bis der NPC entfernt wird.

Beispiel: Blitzschlag bei Interaktion

return {
api_version = 1,

on_npc_interact = function(context)
-- NPC scripts can now reach the full world API.
context.world:strike_lightning_at_location(context.npc:get_location())
end
}

context.npc

context.npc ist in jedem NPC-Hook verfügbar.

Felder

FeldTypHinweise
namestringNPC-Anzeigename aus der Konfiguration.
filenamestringDateiname der NPC-Konfiguration.
uuidstringLaufzeit-UUID des NPCs.
activation_radiusnumberKonfigurierter Aktivierungsradius.
current_locationlocation tableMomentaufnahme der Position, wenn die zugrunde liegende Entity existiert.
entity_typestringBukkit-Entity-Typ, wenn die zugrunde liegende Entity existiert.

Methoden

MethodeArgumenteRückgabeHinweise
is_valid()-booleanOb der NPC noch eine gültige zugrunde liegende Entity hat.
get_location()-location tableAktuelle NPC-Position oder Spawn-Position, wenn die Entity nicht verfügbar ist.
get_eye_location()-location tableAktuelle Augenposition oder Rückfall auf die Spawn-Position.
get_activation_radius()-numberAktuell konfigurierter Aktivierungsradius.
get_nearby_players(radius)numbertableSpieler-Wrapper innerhalb des Radius um den NPC.
face_direction_or_location(target)vector oder locationnilBlickt in einen Richtungsvektor oder dreht sich zu einer Position/Spielerposition.
say_greeting(player?)player, UUID, Name oder nilnilSendet eine konfigurierte Begrüßung. Standardmäßig an den auslösenden Spieler, sofern verfügbar.
say_dialog(player?)player, UUID, Name oder nilnilSendet konfigurierten Dialog. Standardmäßig an den auslösenden Spieler, sofern verfügbar.
say_farewell(player?)player, UUID, Name oder nilnilSendet konfigurierten Abschiedstext. Standardmäßig an den auslösenden Spieler, sofern verfügbar.
play_model_animation(name)stringnilSpielt eine benutzerdefinierte Modellanimation ab, sofern vorhanden. Andernfalls ein gefahrloser No-Op.
patrol_pause()-booleanPausiert die konfigurierte Patrouille.
patrol_resume()-booleanBeendet einen Skript-Halt oder temporären Lauf und setzt die Patrouille fort.
walk_to(x, y, z)drei ZahlenbooleanLäuft zu einem Versatz vom Ursprung und setzt danach die Patrouille fort. Lange Wege werden automatisch gelöst.
hold(x, y, z)drei ZahlenbooleanLäuft zu einem Versatz und bleibt dort.
teleport(x, y, z)drei ZahlenbooleanTeleportiert, wenn am Ziel Entitäten ticken.

Bewegungsmethoden geben false zurück, wenn der NPC keine konfigurierte Patrouille besitzt oder die Anfrage nicht angenommen werden kann. Siehe NPC- und Boss-Patrouillen.


context.player

context.player ist in on_npc_interact, on_npc_proximity_enter und on_npc_proximity_leave verfügbar. In Lebenszyklus-Hooks ohne Spielerbeteiligung ist es nil.

Es handelt sich um den gemeinsamen MagmaCore-Spieler-Wrapper — dieselbe vollständige Living-Entity-/Spieler-Tabelle, die Boss-Powers und FMM-Scripts verwenden, sodass sie weit mehr als die Grundlagen bereitstellt (Leben, Trankeffekte, send_message, show_title, show_action_bar, get_held_item, Raycasting und mehr). Die vollständige Liste finden Sie in der Lua-API-Referenz. Häufig hier verwendet:

Feld / MethodeHinweise
nameSpielername.
uuidSpieler-UUID.
current_locationTabelle mit der aktuellen Spielerposition; ein Feld, keine Methode.
get_eye_location()Aktuelle Augenposition des Spielers.
send_message(text)Sendet eine Chatnachricht. Unterstützt Farbcodes.

Prüfen Sie context.player immer auf nil, bevor Sie es in gemeinsamen Hilfsfunktionen verwenden.

entity_type ist hier kleingeschrieben

Bei gemeinsamen MagmaCore-Entitätstabellen ist entity_type der Bukkit-Name in Kleinbuchstaben ("player", "zombie"). Nur context.npc.entity_type und die Entitätstabellen von EliteMobs-Boss-Powers verwenden die Großschreibung. Vergleichen Sie ohne Beachtung der Groß-/Kleinschreibung, wenn ein Script mit beidem umgehen muss.

EliteMobs-Felder, die jeder gemeinsamen Entitätstabelle hinzugefügt werden

Solange EliteMobs läuft, steuert es zusätzliche Felder zu jeder MagmaCore-Entitätstabelle bei -- context.player, den von context.npc:get_nearby_players(...) zurückgegebenen Wrappern und denen, die FreeMinecraftModels-Prop- und Item-Scripts sehen:

FeldTypHinweise
is_elitebooleantrue, wenn EliteMobs die Entität als Elite verfolgt
is_custom_bossbooleantrue, wenn es sich um einen Custom Boss handelt (immer false, wenn is_elite false ist)
is_significant_bossbooleantrue bei einem Custom Boss, dessen Gesundheitsmultiplikator über 1 liegt -- die praktische Prüfung „das ist ein echter Boss, keine Verstärkung“
eliteTabelle oder nilNur bei Elites vorhanden. Siehe unten

Die elite-Untertabelle:

Feld / MethodeTypHinweise
elite.levelnumberElite-Level
elite.namestring oder nilAnzeigename des Elites
elite.healthnumberAktuelle Gesundheit des Elites (live gelesen)
elite.max_healthnumberMaximale Gesundheit des Elites (live gelesen)
elite.is_custom_bossbooleanDerselbe Wert wie das Feld auf oberster Ebene
elite.health_multipliernumberKonfigurierter Gesundheitsmultiplikator
elite.damage_multipliernumberKonfigurierter Schadensmultiplikator
elite:remove()Despawnt den Elite
-- Warn the approaching player if a real boss is loose near this NPC
on_npc_proximity_enter = function(context)
if context.player == nil then return end
local here = context.npc:get_location()
local nearby = context.world:get_nearby_entities(here.x, here.y, here.z, 40)
for i = 1, #nearby do
if nearby[i].is_significant_boss then
context.player:send_message("&cA boss is nearby: " .. tostring(nearby[i].elite.name))
return
end
end
end

Diese Felder erscheinen nicht auf den Entitäts-Wrappern von EliteMobs-Boss-Powers, die von einem separaten bossseitigen Tabellen-Builder erzeugt werden -- siehe Bosse & Entitäten für diesen Satz.


context.event

context.event ist nil, wenn der Hook kein Bukkit-Event hat. Ist es vorhanden, handelt es sich um die gemeinsame MagmaCore-Event-Tabelle:

Feld / MethodeHinweise
is_cancelledOb das zugrunde liegende Event abgebrochen ist (nur bei abbrechbaren Events aussagekräftig).
cancel()Bricht das Event ab, sofern es abbrechbar ist.
uncancel()Hebt den Abbruch des Events auf, sofern es abbrechbar ist.
playerDer Akteur des Events (z. B. der interagierende Spieler) als Spieler-Wrapper, sofern vorhanden.

Für den interagierenden Spieler bzw. den Näherungs-Spieler ist context.player vorzuziehen (es ist für diese Hooks gesetzt).


State, Scheduler und Abklingzeiten

context.state ist eine einfache Lua-Tabelle, die für diese NPC-Script-Instanz bestehen bleibt, bis der NPC entfernt wird.

context.scheduler ist der gemeinsame MagmaCore-Scheduler. Sowohl die MagmaCore-Namen als auch die EliteMobs-Namen run_after / run_every funktionieren — sie sind Aliase für dasselbe Verhalten:

MethodeArgumenteHinweise
run_later(ticks, callback) / run_after(ticks, callback)number, functionLäuft einmal nach einer Verzögerung. Gibt eine Task-ID zurück.
run_repeating(delay, interval, callback)number, number, functionLäuft nach einer anfänglichen Verzögerung wiederholt. Gibt eine Task-ID zurück.
run_every(interval, callback)number, functionLäuft alle interval Ticks (anfängliche Verzögerung 0). Gibt eine Task-ID zurück.
cancel(task_id) / cancel_task(task_id)numberBricht einen eigenen Task ab.

Scheduler-Callbacks erhalten einen frischen Context. Sie erhalten nicht das ursprüngliche context.player oder context.event. Alle eigenen Tasks werden automatisch abgebrochen, wenn der NPC entfernt wird.

context.cooldowns ist die gemeinsame MagmaCore-Abklingzeit-Tabelle:

MethodeArgumenteRückgabeHinweise
local_ready(key?)stringbooleanTrue, wenn die lokale Abklingzeit abgelaufen ist.
local_remaining(key?)stringnumberVerbleibende Ticks oder 0, wenn bereit.
check_local(key?, duration)string, numberbooleanStartet die Abklingzeit, wenn bereit, und gibt true zurück.
set_local(duration, key?)number, stringnilSetzt die Abklingzeit oder setzt sie zurück.
global_ready()-booleanTrue, wenn die gemeinsame globale Abklingzeit bereit ist.
set_global(duration)numbernilStartet die globale Abklingzeit.
Unified cooldown API

NPC-Scripts verwenden jetzt die gemeinsame MagmaCore-Abklingzeit-Reihenfolge (check_local(key?, duration)), genau wie Boss-Powers und FreeMinecraftModels-Scripts. Frühere experimentelle NPC-Builds verwendeten check_local(duration, key?) — aktualisieren Sie alte Scripts auf die gemeinsame Reihenfolge.


Beispiel: Winken bei Annäherung

Dieses Script lässt den NPC den eintretenden Spieler ansehen und die benutzerdefinierte Modellanimation wave abspielen. Proximity-Enter wird bereits einmal pro NPC-/Spieler-Paar ausgelöst, solange der Spieler innerhalb des Radius bleibt; die Abklingzeit verhindert, dass schnelle Verlassen-/Wiedereintreten-Zyklen die Animation zu oft wiederholen.

return {
api_version = 1,
priority = 0,

on_npc_proximity_enter = function(context)
if context.player == nil then return end

if context.cooldowns:check_local("wave:" .. context.player.uuid, 60) then
context.npc:face_direction_or_location(context.player.current_location)
context.npc:play_model_animation("wave")
end
end
}

play_model_animation(name) tut gefahrlos nichts, wenn der NPC kein benutzerdefiniertes Modell hat oder das Modell diese Animation nicht besitzt.


Performance-Richtlinien

  • Halten Sie on_game_tick-Hooks klein. Sie laufen 20-mal pro Sekunde für jede NPC-Script-Instanz, die sie definiert. (Scripts, die on_game_tick nicht deklarieren, werden niemals getickt.)
  • Bevorzugen Sie on_npc_proximity_enter und on_npc_proximity_leave für Näherungsverhalten, statt in jedem Tick nahe Spieler abzufragen.
  • Verwenden Sie context.cooldowns:check_local(...), um Animationen, Sounds und Partikelausbrüche zu drosseln.
  • Verwenden Sie context.scheduler:run_repeating(...) mit einem sinnvollen Intervall, wenn ein Verhalten nicht in jedem Tick laufen muss.
  • Vermeiden Sie große Suchvorgänge in Lua. context.npc:get_nearby_players(radius) ist für kleine lokale Prüfungen in Ordnung, aber breites Scannen sollte in der Plugin-Laufzeitumgebung bleiben.

Verwandte Seiten