Lua-Scripting: NPC-Scripts
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.player — plus eine NPC-spezifische context.npc-Tabelle. Alles, was MagmaCore für Scripts bereitstellt, ist auch hier verfügbar.
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:
| Feld | Typ | Hinweise |
|---|---|---|
api_version | number | Erforderlich. Muss 1 sein. |
priority | number | Optional. Niedrigere Werte laufen zuerst. |
on_spawn | function | Läuft, nachdem der NPC gespawnt ist. |
on_remove | function | Läuft, wenn der NPC entfernt wird. |
on_game_tick | function | Läuft in jedem Server-Tick, solange der NPC gültig ist. Halten Sie dies sehr schlank. |
on_npc_interact | function | Läuft, wenn ein Spieler mit dem NPC interagiert. |
on_npc_proximity_enter | function | Läuft einmal, wenn ein Spieler den Aktivierungsradius dieses NPCs betritt. |
on_npc_proximity_leave | function | Läuft einmal, wenn ein Spieler den Aktivierungsradius dieses NPCs verlässt. |
on_zone_enter | function | Läuft, wenn ein Spieler eine Zone betritt, die dieses Script überwacht (siehe context.zones). |
on_zone_leave | function | Lä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.
| Hook | Wird ausgelöst, wenn |
|---|---|
on_npc_proximity_enter | Ein Spieler sich von außerhalb des Aktivierungsradius des NPCs nach innen bewegt. |
on_npc_proximity_leave | Ein 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:
| Tabelle | Wofür sie da ist |
|---|---|
context.world | Welteffekte 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.zones | Erstellt 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.scheduler | run_later(ticks, fn), run_repeating(delay, interval, fn), cancel(task_id). |
context.cooldowns | Gemeinsame MagmaCore-Abklingzeiten: local_ready, local_remaining, check_local, set_local, global_ready, set_global. |
context.log | info(msg), warn(msg), error(msg) — schreibt in die Serverkonsole. |
context.event | Das aktuelle Bukkit-Event, sofern vorhanden. Siehe unten. |
context.player | Der interagierende/auslösende Spieler, sofern vorhanden. Siehe unten. |
context.state | Eine 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
| Feld | Typ | Hinweise |
|---|---|---|
name | string | NPC-Anzeigename aus der Konfiguration. |
filename | string | Dateiname der NPC-Konfiguration. |
uuid | string | Laufzeit-UUID des NPCs. |
activation_radius | number | Konfigurierter Aktivierungsradius. |
current_location | location table | Momentaufnahme der Position, wenn die zugrunde liegende Entity existiert. |
entity_type | string | Bukkit-Entity-Typ, wenn die zugrunde liegende Entity existiert. |
Methoden
| Methode | Argumente | Rückgabe | Hinweise |
|---|---|---|---|
is_valid() | - | boolean | Ob der NPC noch eine gültige zugrunde liegende Entity hat. |
get_location() | - | location table | Aktuelle NPC-Position oder Spawn-Position, wenn die Entity nicht verfügbar ist. |
get_eye_location() | - | location table | Aktuelle Augenposition oder Rückfall auf die Spawn-Position. |
get_activation_radius() | - | number | Aktuell konfigurierter Aktivierungsradius. |
get_nearby_players(radius) | number | table | Spieler-Wrapper innerhalb des Radius um den NPC. |
face_direction_or_location(target) | vector oder location | nil | Blickt in einen Richtungsvektor oder dreht sich zu einer Position/Spielerposition. |
say_greeting(player?) | player, UUID, Name oder nil | nil | Sendet eine konfigurierte Begrüßung. Standardmäßig an den auslösenden Spieler, sofern verfügbar. |
say_dialog(player?) | player, UUID, Name oder nil | nil | Sendet konfigurierten Dialog. Standardmäßig an den auslösenden Spieler, sofern verfügbar. |
say_farewell(player?) | player, UUID, Name oder nil | nil | Sendet konfigurierten Abschiedstext. Standardmäßig an den auslösenden Spieler, sofern verfügbar. |
play_model_animation(name) | string | nil | Spielt eine benutzerdefinierte Modellanimation ab, sofern vorhanden. Andernfalls ein gefahrloser No-Op. |
patrol_pause() | - | boolean | Pausiert die konfigurierte Patrouille. |
patrol_resume() | - | boolean | Beendet einen Skript-Halt oder temporären Lauf und setzt die Patrouille fort. |
walk_to(x, y, z) | drei Zahlen | boolean | Läuft zu einem Versatz vom Ursprung und setzt danach die Patrouille fort. Lange Wege werden automatisch gelöst. |
hold(x, y, z) | drei Zahlen | boolean | Läuft zu einem Versatz und bleibt dort. |
teleport(x, y, z) | drei Zahlen | boolean | Teleportiert, 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 / Methode | Hinweise |
|---|---|
name | Spielername. |
uuid | Spieler-UUID. |
current_location | Tabelle 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 kleingeschriebenBei 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:
| Feld | Typ | Hinweise |
|---|---|---|
is_elite | boolean | true, wenn EliteMobs die Entität als Elite verfolgt |
is_custom_boss | boolean | true, wenn es sich um einen Custom Boss handelt (immer false, wenn is_elite false ist) |
is_significant_boss | boolean | true bei einem Custom Boss, dessen Gesundheitsmultiplikator über 1 liegt -- die praktische Prüfung „das ist ein echter Boss, keine Verstärkung“ |
elite | Tabelle oder nil | Nur bei Elites vorhanden. Siehe unten |
Die elite-Untertabelle:
| Feld / Methode | Typ | Hinweise |
|---|---|---|
elite.level | number | Elite-Level |
elite.name | string oder nil | Anzeigename des Elites |
elite.health | number | Aktuelle Gesundheit des Elites (live gelesen) |
elite.max_health | number | Maximale Gesundheit des Elites (live gelesen) |
elite.is_custom_boss | boolean | Derselbe Wert wie das Feld auf oberster Ebene |
elite.health_multiplier | number | Konfigurierter Gesundheitsmultiplikator |
elite.damage_multiplier | number | Konfigurierter 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 / Methode | Hinweise |
|---|---|
is_cancelled | Ob 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. |
player | Der 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:
| Methode | Argumente | Hinweise |
|---|---|---|
run_later(ticks, callback) / run_after(ticks, callback) | number, function | Läuft einmal nach einer Verzögerung. Gibt eine Task-ID zurück. |
run_repeating(delay, interval, callback) | number, number, function | Läuft nach einer anfänglichen Verzögerung wiederholt. Gibt eine Task-ID zurück. |
run_every(interval, callback) | number, function | Läuft alle interval Ticks (anfängliche Verzögerung 0). Gibt eine Task-ID zurück. |
cancel(task_id) / cancel_task(task_id) | number | Bricht 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:
| Methode | Argumente | Rückgabe | Hinweise |
|---|---|---|---|
local_ready(key?) | string | boolean | True, wenn die lokale Abklingzeit abgelaufen ist. |
local_remaining(key?) | string | number | Verbleibende Ticks oder 0, wenn bereit. |
check_local(key?, duration) | string, number | boolean | Startet die Abklingzeit, wenn bereit, und gibt true zurück. |
set_local(duration, key?) | number, string | nil | Setzt die Abklingzeit oder setzt sie zurück. |
global_ready() | - | boolean | True, wenn die gemeinsame globale Abklingzeit bereit ist. |
set_global(duration) | number | nil | Startet die globale Abklingzeit. |
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, dieon_game_ticknicht deklarieren, werden niemals getickt.) - Bevorzugen Sie
on_npc_proximity_enterundon_npc_proximity_leavefü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
- NPCs erstellen -- NPC-Konfigurationsfelder, einschließlich
activationRadius - Lua: Erste Schritte -- Boss-Lua-Powers
- Scripting-Engine -- gemeinsame Lua-Konzepte und die vereinheitlichte Laufzeitumgebung
- Lua-API-Referenz -- vollständige Methodenliste für
context.world,context.player,context.zonesund mehr
