Lua-Scripting: Beispiele & Muster
Diese Seite enthält vollständige funktionierende Beispiele von EliteMobs Lua-Fähigkeiten sowie praktische Muster, Best Practices und Tipps. Jedes Beispiel enthält eine Erklärung, was es tut und warum.
Wenn Sie neu bei Lua-Fähigkeiten sind, beginnen Sie mit Erste Schritte. Für vollständige API-Details siehe die API-Referenz, Boss & Entitäten, Welt & Umgebung, Zonen & Zielerfassung und Enums.
Beispiel: Zonenbasierte Zielerfassung mit Script-Utilities
Was dieses Beispiel lehrt: Wie man context.script verwendet, um EliteScript-artige Zonengeometrie aus Lua zu erstellen, Partikel zu spawnen und Entitäten in der Zone Schaden zuzufügen.
Vollständige Power-Datei (klicken zum Aufklappen)
return {
api_version = 1,
on_boss_damaged_by_player = function(context)
local cone = context.script:zone({
shape = "CONE",
Target = {
targetType = "SELF",
offset = "0,1,0"
},
Target2 = {
targetType = "NEARBY_PLAYERS",
range = 20
},
radius = 5
})
context.script:spawn_particles(
cone:full_target(0.4),
{
particle = "FLAME",
amount = 1,
speed = 0.05
}
)
context.script:damage(cone:full_target(), 1.0, 1.5)
end
}
Erklärung
-
Zonenerstellung --
context.script:zone(...)erstellt eine Kegelform unter Verwendung derselben Feldnamen wie EliteScript-Zonen.Targetsetzt den Kegelursprung (der Boss selbst, um 1 Block nach oben versetzt), undTarget2setzt das Ziel (die nächsten Spieler innerhalb von 20 Blöcken).radiussteuert, wie breit sich der Kegel öffnet. -
Partikel-Spawning --
cone:full_target(0.4)gibt ein Ziel-Handle zurück, das zu allen Positionen innerhalb des Kegels mit 40% Abdeckung aufgelöst wird (bei jedem Aufruf werden zufällig 40% der Zonenpunkte abgetastet). Die Partikelspezifikation verwendet dieselben Feldnamen wie EliteScript-Partikel:particle,amountundspeed. -
Schaden --
context.script:damage(cone:full_target(), 1.0, 1.5)trifft alle lebenden Entitäten im gesamten Kegel. Die erste Zahl (1.0) ist der Basisschaden, und die zweite (1.5) ist der auf Spieler angewendete Schadensmultiplikator.
Die an context.script übergebenen Zonen- und Zieltabellen verwenden EliteScript-Feldnamen (targetType, shape, Target, Target2, range, offset, coverage). Die vollständige Liste finden Sie unter EliteScript-Zonen und EliteScript-Ziele.
Beispiel: Zustand + Scheduler-Angriffsschleife
Was dieses Beispiel lehrt: Verwendung von context.state zur Verfolgung des Laufzeitzustands, context.scheduler für wiederholende Aufgaben und ordnungsgemäßen Kampfstart/Ende-Lebenszyklus.
Vollständige Power-Datei (klicken zum Aufklappen)
local function pick_action(context)
local roll = math.random(1, 2)
if roll == 1 then
context.boss:play_model_animation("slam")
else
context.boss:play_model_animation("roar")
end
end
return {
api_version = 1,
on_spawn = function(context)
context.state.started = false
context.state.loop_task_id = nil
end,
on_enter_combat = function(context)
if context.state.started then
return
end
context.state.started = true
context.state.loop_task_id = context.scheduler:run_every(100, function(loop_context)
if loop_context.boss.exists then
pick_action(loop_context)
end
end)
end,
on_exit_combat = function(context)
if context.state.loop_task_id ~= nil then
context.scheduler:cancel_task(context.state.loop_task_id)
context.state.loop_task_id = nil
end
context.state.started = false
end
}
Erklärung
-
Zustandsinitialisierung --
on_spawnsetztcontext.state.startedauffalseundcontext.state.loop_task_idaufnil. Diestate-Tabelle bleibt für die gesamte Lebensdauer dieser Boss-Instanz bestehen, sodass hier gesetzte Werte über Hooks hinweg erhalten bleiben. -
Kampfschutz --
on_enter_combatprüftcontext.state.started, bevor die Schleife gestartet wird. Dies verhindert mehrere überlappende Schleifen, wenn das Ereignis mehr als einmal ausgelöst wird. -
Scheduler-Muster --
context.scheduler:run_every(100, callback)führt den Callback alle 100 Ticks (5 Sekunden) aus. Der Callback erhält einen frischen Kontext, sodassloop_context.bossden neuesten Boss-Snapshot liefert. Der Scheduler gibt eine numerische Task-ID zurück, die Sie im State speichern. -
Aufräumen beim Verlassen --
on_exit_combatbricht die wiederholende Aufgabe mit der gespeicherten Task-ID ab und setzt den State zurück. Dies ist kritisch: Ohne Aufräumen läuft der Scheduler auch nach Kampfende weiter.
Wenn Sie eine wiederholende Aufgabe in on_enter_combat starten, brechen Sie sie immer in on_exit_combat ab. Das Vergessen des Abbrechens hinterlässt eine Hintergrundaufgabe, die bis zum Despawn des Bosses läuft, was Leistung verschwendet und unerwartetes Verhalten verursachen kann.
Beispiel: Feuereffekt bei Treffer
Was dieses Beispiel lehrt: Ein einfacher „bei Treffer Effekt anwenden"-Power -- das häufigste Muster für Kampffähigkeiten.
Vollständige Power-Datei (klicken zum Aufklappen)
return {
api_version = 1,
on_player_damaged_by_boss = function(context)
-- Guard: the player may be nil in edge cases
if context.player == nil then
return
end
-- Check and set a 60-tick (3 second) local cooldown in one call
if not context.cooldowns:check_local("fire_touch", 60) then
return
end
-- Set the player on fire for 60 ticks (3 seconds)
context.player:set_fire_ticks(60)
-- Visual feedback: spawn flame particles at the player's location
context.world:spawn_particle_at_location(
context.player.current_location,
{ particle = "FLAME", amount = 20, speed = 0.1 }
)
-- Tell the player what happened
context.player:send_message("&cThe boss's touch burns!")
-- Set the global power cooldown so other powers on this boss
-- don't all fire at the same instant
context.cooldowns:set_global(40)
end
}
Erklärung
-
Nil-Schutz --
context.playerist ein Lazy Key, der zum am Ereignis beteiligten Spieler aufgelöst wird. In seltenen Grenzfällen (z.B. der Spieler hat sich zwischen Ereignisauslösung und Hook-Ausführung abgemeldet) kann ernilsein. Prüfen Sie immer, bevor Sie ihn verwenden. -
Lokaler Cooldown --
context.cooldowns:check_local("fire_touch", 60)macht zwei Dinge atomar: Es prüft, ob der Cooldown-Schlüssel"fire_touch"bereit ist, und wenn ja, setzt es den Cooldown sofort auf 60 Ticks. Wenn der Cooldown nicht bereit ist, gibt esfalsezurück und die Funktion beendet sich vorzeitig. Der Schlüssel"fire_touch"ist auf diese Boss-Instanz beschränkt -- andere Bosse mit derselben Fähigkeit haben unabhängige Cooldowns. -
Feuer-Ticks --
context.player:set_fire_ticks(60)setzt den Spieler für 60 Spielticks (3 Sekunden) in Brand. Dies ruft direkt die zugrunde liegende Bukkit-Methode auf. -
Partikel --
context.world:spawn_particle_at_location(location, spec)spawnt Partikel an einer bestimmten Position. Die Spec-Tabelle akzeptiertparticle(Bukkit-Partikel-Enum-Name),amountundspeed. -
Nachricht --
context.player:send_message(text)sendet eine farbcodierte Chat-Nachricht. Standard-Minecraft-Farbcodes wie&c(rot) funktionieren automatisch. -
Globaler Cooldown --
context.cooldowns:set_global(40)setzt alle Fähigkeiten dieses Bosses auf einen 40-Tick (2 Sekunden) Cooldown. Dies verhindert, dass mehrere Fähigkeiten gleichzeitig ausgelöst werden.
Beispiel: Zonenbasierte AoE-Fähigkeit mit nativen Lua-Zonen
Was dieses Beispiel lehrt: Erstellen und Abfragen nativer Lua-Zonen, um Spielern in einem Bereich Schaden zuzufügen.
Vollständige Power-Datei (klicken zum Aufklappen)
return {
api_version = 1,
on_spawn = function(context)
context.state.aoe_task_id = nil
end,
on_enter_combat = function(context)
-- Prevent duplicate loops
if context.state.aoe_task_id ~= nil then
return
end
context.state.aoe_task_id = context.scheduler:run_every(60, function(tick_context)
-- Make sure the boss is still alive
if not tick_context.boss.exists then
return
end
-- Check a local cooldown so this doesn't stack with other effects
if not tick_context.cooldowns:check_local("pulse_aoe", 60) then
return
end
-- Build a sphere zone centered on the boss's current position
local zone_def = {
kind = "sphere",
radius = 8,
origin = tick_context.boss:get_location()
}
-- Find all players inside the sphere
local victims = tick_context.zones:get_entities_in_zone(zone_def, { filter = "players" })
-- Damage and show particles on each player found
for i = 1, #victims do
local victim = victims[i]
victim:deal_custom_damage(4.0)
tick_context.world:spawn_particle_at_location(
victim.current_location,
{ particle = "DUST", amount = 15, speed = 0, red = 128, green = 0, blue = 255 }
)
end
-- Spawn visual ring particles at the boss
tick_context.world:spawn_particle_at_location(
tick_context.boss:get_location(),
{ particle = "SPELL_MOB", amount = 40, speed = 0.1 }
)
-- Set global cooldown
tick_context.cooldowns:set_global(60)
end)
end,
on_exit_combat = function(context)
if context.state.aoe_task_id ~= nil then
context.scheduler:cancel_task(context.state.aoe_task_id)
context.state.aoe_task_id = nil
end
end
}
Erklärung
-
Zustandseinrichtung --
on_spawninitialisiertaoe_task_idmitnil. Diese Task-ID wird die Referenz auf die wiederholende Scheduler-Aufgabe halten. -
Wiederholender Angriff --
on_enter_combatstartet eine wiederholende Aufgabe alle 60 Ticks (3 Sekunden). Der Schutz am Anfang verhindert das Starten einer zweiten Schleife, wenn der Kampf erneut betreten wird. -
Zonendefinition -- Die
zone_def-Tabelle verwendet die native Lua-Zonensyntax. Daskind-Feld gibt die Form an ("sphere"),radiussetzt die Größe, undoriginwird auf die aktuelle Position des Bosses zum Zeitpunkt des Callback-Aufrufs gesetzt. Das bedeutet, die Zone folgt dem Boss, wenn er sich bewegt. -
Entitätsabfrage --
tick_context.zones:get_entities_in_zone(zone_def, { filter = "players" })gibt ein Lua-Array aller Spielertabellen innerhalb der Kugel zurück. Diefilter-Option akzeptiert"players","elites","mobs"oder"living"(Standard). -
Schaden verursachen --
victim:deal_custom_damage(4.0)verursacht 4 Schadenspunkte, die dem Boss zugeschrieben werden. Dies verwendet das benutzerdefinierte EliteMobs-Schadenssystem, das Rüstung und andere Kampfmodifikatoren berücksichtigt. -
Partikel-Feedback -- Lila Staubpartikel erscheinen auf jedem Opfer, und Umgebungspartikel erscheinen an der Position des Bosses, um den Puls visuell zu signalisieren.
-
Aufräumen --
on_exit_combatbricht die wiederholende Aufgabe ab und bereinigt den State, dem gleichen Muster wie im vorherigen Beispiel folgend.
Dieses Beispiel verwendet native Lua-Zonen (context.zones:get_entities_in_zone()), die eine einfache Tabelle mit kind, radius, origin usw. nehmen. Die Script-Utilities (context.script:zone(...)) verwenden EliteScript-Feldnamen wie shape, Target und Target2. Beide funktionieren -- verwenden Sie native Zonen für einfache Formen und context.script, wenn Sie EliteScripts erweiterte Zielauflösung benötigen.
Beispiel: Mehrstufige Boss-Mechanik
Was dieses Beispiel lehrt: Verwendung von State zur Verfolgung von Boss-Phasen und Verhaltensänderung bei Lebensschwellenwerten.
Vollständige Power-Datei (klicken zum Aufklappen)
local function phase_one_attack(context)
-- Slow, heavy slam
context.boss:play_model_animation("slam")
local zone_def = {
kind = "sphere",
radius = 5,
origin = context.boss:get_location()
}
local targets = context.zones:get_entities_in_zone(zone_def, { filter = "players" })
for i = 1, #targets do
targets[i]:deal_custom_damage(3.0)
end
context.world:spawn_particle_at_location(
context.boss:get_location(),
{ particle = "EXPLOSION", amount = 3, speed = 0 }
)
end
local function phase_two_attack(context)
-- Fast, frantic multi-hit
context.boss:play_model_animation("frenzy")
local zone_def = {
kind = "sphere",
radius = 8,
origin = context.boss:get_location()
}
local targets = context.zones:get_entities_in_zone(zone_def, { filter = "players" })
for i = 1, #targets do
targets[i]:deal_custom_damage(2.0)
targets[i]:apply_potion_effect("SLOWNESS", 40, 1)
end
context.world:spawn_particle_at_location(
context.boss:get_location(),
{ particle = "DUST", amount = 30, speed = 0.2, red = 255, green = 0, blue = 0 }
)
context.world:play_sound_at_location(
context.boss:get_location(),
"entity.wither.ambient",
1.0, 1.5
)
end
return {
api_version = 1,
on_spawn = function(context)
context.state.phase = 1
context.state.attack_task_id = nil
context.state.phase_switched = false
end,
on_enter_combat = function(context)
if context.state.attack_task_id ~= nil then
return
end
-- Start the phase 1 attack loop: every 100 ticks (5 seconds)
context.state.attack_task_id = context.scheduler:run_every(100, function(tick_context)
if not tick_context.boss.exists then
return
end
if not tick_context.cooldowns:check_local("phase_attack", 100) then
return
end
phase_one_attack(tick_context)
end)
end,
on_game_tick = function(context)
-- Only check phase transition every 20 ticks (1 second) to stay lightweight
if not context.cooldowns:check_local("phase_check", 20) then
return
end
-- Skip if already in phase 2
if context.state.phase ~= 1 then
return
end
-- Check health ratio
local health_ratio = context.boss.health / context.boss.maximum_health
if health_ratio <= 0.5 then
-- Transition to phase 2
context.state.phase = 2
context.state.phase_switched = true
context.log:info("Boss entering phase 2 at " .. tostring(math.floor(health_ratio * 100)) .. "% health")
-- Cancel the old attack loop
if context.state.attack_task_id ~= nil then
context.scheduler:cancel_task(context.state.attack_task_id)
context.state.attack_task_id = nil
end
-- Play transition effects
context.boss:play_model_animation("transform")
-- Announce the phase change to nearby players
local nearby = context.players.nearby_players(40)
for i = 1, #nearby do
nearby[i]:send_message("&4&lThe boss enters a frenzy!")
nearby[i]:show_title("&4Phase 2", "&cThe boss is enraged!", 10, 40, 10)
end
-- Start a faster phase 2 attack loop: every 40 ticks (2 seconds)
context.state.attack_task_id = context.scheduler:run_every(40, function(tick_context)
if not tick_context.boss.exists then
return
end
if not tick_context.cooldowns:check_local("phase_attack", 40) then
return
end
phase_two_attack(tick_context)
end)
end
end,
on_exit_combat = function(context)
if context.state.attack_task_id ~= nil then
context.scheduler:cancel_task(context.state.attack_task_id)
context.state.attack_task_id = nil
end
end
}
Erklärung
-
Zustandsinitialisierung --
on_spawnsetztphaseauf1,attack_task_idaufnilundphase_switchedauffalse. Diese Werte bleiben über alle Hooks für diese Boss-Instanz bestehen. -
Phase-1-Angriffsschleife --
on_enter_combatstartet eine wiederholende Aufgabe, diephase_one_attackalle 100 Ticks ausführt. Der Angriff erstellt eine 5-Block-Kugel, schadet Spielern darin und spielt eine Slam-Animation mit Explosionspartikeln ab. -
Phasenprüfung in on_game_tick --
on_game_tickwird jeden einzelnen Server-Tick ausgelöst, daher prüft es zuerst einen 20-Tick-Cooldown ("phase_check"), um aufwendige Logik nicht jeden Tick auszuführen. Wenn der Boss bereits in Phase 2 ist, wird vorzeitig beendet. -
Lebensschwellenwert --
context.boss.health / context.boss.maximum_healthgibt die aktuelle Gesundheit als Bruch an. Wenn sie auf 50% oder darunter fällt, beginnt der Übergang. -
Phasenübergang -- Die alte Angriffsschleife wird abgebrochen, eine Transformationsanimation wird abgespielt, nahegelegene Spieler erhalten eine Nachricht und einen Titel, und eine neue schnellere Angriffsschleife startet (alle 40 Ticks statt 100).
-
Phase-2-Angriffe --
phase_two_attackverwendet eine größere Kugel (8 Blöcke), verursacht etwas weniger Schaden pro Treffer, feuert aber viel häufiger, wendet einen Langsamkeitseffekt an und verwendet rote Staubpartikel und einen Wither-Sound für ein anderes Gefühl. -
Protokollierung --
context.log:info(...)schreibt in die Serverkonsole, was für das Debugging von Phasenübergängen während der Entwicklung unschätzbar wertvoll ist. -
Aufräumen --
on_exit_combatbricht die aktuell aktive Angriffsschleife ab, unabhängig davon, in welcher Phase sich der Boss befindet.
on_game_tick wird jeden Server-Tick ausgeführt (20 Mal pro Sekunde). Schützen Sie aufwendige Arbeit immer mit einer Cooldown-Prüfung, wie mit check_local("phase_check", 20) gezeigt. Wenn Ihr Hook 50ms überschreitet, wird EliteMobs die Fähigkeit automatisch deaktivieren.
KI-Generierungstipps
Wenn Sie möchten, dass KI Lua-Fähigkeiten zuverlässig generiert, stellen Sie sicher, dass der Prompt Folgendes enthält:
- Exakter Hook-Name -- z.B.
on_player_damaged_by_boss, nicht „wenn der Boss einen Spieler trifft". - Native Lua-Zonen oder Script-Utilities -- geben Sie an, welche. Native Zonen verwenden
context.zones:get_entities_in_zone(zone_def, opts)mitkind,radius,origin. Script-Utilities verwendencontext.script:zone(...)mit EliteScript-Feldnamen wieshape,Target,Target2. - Lokale und globale Cooldowns -- geben Sie den Cooldown-Schlüsselnamen und die Dauer in Ticks an.
context.cooldowns:check_local(key, ticks)für Boss-spezifische Cooldowns,context.cooldowns:set_global(ticks)für den gemeinsamen Power-Cooldown. - Benutzerdefinierte Modellanimationsnamen -- z.B.
context.boss:play_model_animation("slam"). Die KI kann keine Animationsnamen erraten; stellen Sie sie bereit. - Zielauswahl -- geben Sie an, ob die Fähigkeit
context.player(den Ereignisspieler),context.players.nearby_players(range)oder eine Zonenabfrage anvisiert. - Effekttyp -- Schaden (
deal_custom_damage), Trank (apply_potion_effect), Feuer (set_fire_ticks), Geschwindigkeit (set_velocity_vector,apply_push_vector), etc. - Nur dokumentierte Methodennamen verwenden -- wenn es nicht auf der API-Referenzseite steht, existiert es nicht.
- Script-Utility-Specs verwenden EliteScript-Feldnamen --
targetType,shape,Target,Target2,range,offset,coverage, nicht Lua-artige Namen. - Scheduler-Callbacks akzeptieren frischen Kontext -- der Callback-Parameter ist ein neuer Kontext, nicht der äußere. Verwenden Sie immer
tick_context(oder wie auch immer Sie den Parameter benennen) innerhalb von Callbacks.
Gutes Prompt-Beispiel
Schreibe eine Lua-Fähigkeit, die
on_enter_combatverwendet, um eine wiederholende Aufgabe alle 80 Ticks zu starten. Erstelle bei jedem Tick eine native Lua-Kugelzone (Radius 6) zentriert auf dem Boss, frage Spieler darin ab und verursache 2.0 benutzerdefinierten Schaden bei jedem. Verwende einen lokalen Cooldown-Schlüssel"pulse"mit Dauer 80. Breche die Aufgabe inon_exit_combatab. Spawne DUST-Partikel (red=0, green=255, blue=100) bei jedem Opfer.
Zusätzliche Einschränkungen zum Einbeziehen
- „Gib eine einzelne Tabelle mit
api_version = 1zurück." - „Initialisiere alle State-Felder in
on_spawn." - „Schütze Scheduler-Callbacks mit
if not tick_context.boss.exists then return end." - „Breche alle wiederholenden Aufgaben in
on_exit_combatab." - „Erfinde keine Methodennamen -- verwende nur Methoden von der API-Referenzseite."
- „Verwende
context.cooldowns:check_local(key, ticks)für kombiniertes Prüfen-und-Setzen."
QC-Checkliste für manuelle oder KI-Überprüfung
Verwenden Sie diese Checkliste, um eine Lua-Fähigkeit vor dem Einsatz zu überprüfen:
- Die Datei gibt genau eine Tabelle mit
api_version = 1zurück. - Jeder Hook-Name stimmt exakt mit einem Eintrag in der Hook-Liste überein (z.B.
on_player_damaged_by_boss, nichton_player_hit). context.playerwird mit== nilgeschützt, bevor es in Hooks verwendet wird, wo es nil sein kann.context.state-Felder werden inon_spawninitialisiert.- Jeder
context.scheduler:run_every(...)-Aufruf hat ein passendescontext.scheduler:cancel_task(...)inon_exit_combat. - Scheduler-Callbacks verwenden den eigenen Kontextparameter des Callbacks, nicht den äußeren
context. - Cooldown-Schlüssel sind beschreibende Strings (z.B.
"fire_pulse") und Dauern sind in Ticks. on_game_tick-Hooks schützen aufwendige Arbeit hinter einer Cooldown-Prüfung.- Alle Methodennamen existieren in der API-Referenz -- keine erfundenen Aliase.
- Script-Utility-Tabellen verwenden EliteScript-Feldnamen (
targetType,shape,Target, etc.), nicht Lua-artige Namen. - Native Zonendefinitionen verwenden
kind,radius,origin,destination, etc. - Die Fähigkeit ruft keine blockierenden oder lang laufenden Operationen innerhalb eines Hooks oder Callbacks auf.
- Partikelspezifikationen verwenden gültige Bukkit-Partikel-Enum-Namen in UPPER_CASE (z.B.
"FLAME","DUST","EXPLOSION").
Best Practices
-
Beginnen Sie mit einem kleinen Hook und verifizieren Sie. Schreiben Sie einen einzelnen
on_spawn, der eine Log-Nachricht sendet. Bestätigen Sie, dass er auslöst. Dann bauen Sie von dort aus weiter. -
Halten Sie Hilfsfunktionen lokal. Deklarieren Sie Helfer wie
local function pick_action(context)über der Return-Tabelle. Dies hält sie aus dem globalen Scope und vermeidet Kollisionen mit anderen Lua-Fähigkeiten, die in derselben Laufzeit geladen werden. -
Legen Sie Geometrie in Script-Utilities. Wenn Sie Kegel, rotierende Strahlen, translierende Strahlen oder animierte Zonen benötigen, verwenden Sie
context.script:zone(...)mit EliteScript-Feldnamen. Die Script-Utilities verwenden die kampferprobte EliteScript-Zonenengine. -
Verwenden Sie
context.statefür Laufzeitzustand. Verwenden Sie keine Lua-Globalvariablen.context.stateist auf eine einzelne Boss-Instanz beschränkt und bleibt über Hooks für die Lebensdauer dieses Bosses bestehen. -
Verwenden Sie benannte lokale Cooldown-Schlüssel. Verwenden Sie anstelle von bloßen Zahlen beschreibende Schlüssel wie
"fire_touch"oder"aoe_pulse". Dies erleichtert das Debugging und verhindert versehentliche Kollisionen zwischen verschiedenen Cooldowns in derselben Fähigkeit. -
Halten Sie
on_game_tickleicht. Schützen Sie es immer hinter einer Cooldown-Prüfung. Wenn Ihre Logik jeden Tick läuft, muss sie in deutlich unter 50ms abgeschlossen sein, oder die Fähigkeit wird deaktiviert. -
Brechen Sie wiederholende Aufgaben ab, wenn sie fertig sind. Jedes
run_everymuss ein passendescancel_taskinon_exit_combathaben (und möglicherweiseon_death). Durchgesickerte Aufgaben verschwenden CPU und können Null-Referenz-Fehler nach dem Despawn des Bosses verursachen. -
Verwenden Sie frische Scheduler-Callback-Kontexte. Scheduler-Callbacks (
run_every,run_after) erhalten einen frischen Kontext als Parameter. Verwenden Sie immer diesen Parameter -- nicht den äußerencontext-- da der äußere Kontext veraltete Snapshots halten kann. -
Protokollieren Sie Zustandsübergänge mit
context.log:info(). Fügen Sie während der Entwicklung Protokollierung für Phasenwechsel, Cooldown-Starts und Scheduler-Starts/Stopps hinzu. Entfernen oder ändern Sie zucontext.log:debug()vor dem Einsatz. -
Verwenden Sie vorhandene EliteScript-Dokumentation wieder. Die Seiten Zonen, Ziele, Relative Vektoren und Bedingungen dokumentieren die gleichen Feldnamen, die die Script-Utilities akzeptieren. Duplizieren Sie diese Informationen nicht in Ihrer Lua-Fähigkeit -- verweisen Sie einfach darauf.
Häufige Anfängerfehler
-
Verwendung des äußeren
contextinnerhalb eines Scheduler-Callbacks. Der äußere Kontext erfasst einen Snapshot zum Zeitpunkt der Hook-Ausführung. Verwenden Sie innerhalb einesrun_every- oderrun_after-Callbacks immer den eigenen Parameter des Callbacks (z.B.tick_context), der Ihnen einen frischen Snapshot gibt. -
Vergessen, wiederholende Aufgaben abzubrechen. Wenn Sie ein
run_everyinon_enter_combatstarten, aber nie abbrechen, läuft die Aufgabe bis der Boss vom Server entfernt wird, auch nach Kampfende. -
State nicht in
on_spawninitialisieren. Wenn Siecontext.state.phaseinon_game_ticklesen, aber nie inon_spawnsetzen, wird esnilsein und Ihre Vergleiche werden sich unerwartet verhalten. -
context.playerohne Nil-Schutz prüfen. In Hooks wieon_player_damaged_by_bossist der Spieler fast immer verfügbar -- aber „fast immer" ist nicht „immer". Ein einziger fehlender Nil-Schutz kann die Fähigkeit zum Absturz bringen. -
Lua-artige Feldnamen in Script-Utility-Aufrufen verwenden. Die Script-Utilities erwarten
targetType,Target,Target2,shape-- nichttarget_type,target,target2,zone_shape. Nicht übereinstimmende Namen erzeugen stillschweigend keine Ergebnisse. -
Schwere Logik in
on_game_tickohne Cooldown-Gate ausführen. Dieser Hook wird jeden Server-Tick ausgelöst. Selbst einfache Arithmetik, die 20 Mal pro Sekunde über viele Bosse hinweg wiederholt wird, summiert sich. -
Methodennamen erfinden. Wenn eine Methode nicht in der API-Referenz aufgeführt ist, existiert sie nicht. Häufige Fehler sind das Schreiben von
entity:teleport(loc)stattentity:teleport_to_location(loc), oderplayer:set_velocity(vec)stattplayer:set_velocity_vector(vec). -
context.boss.healthzum Setzen der Gesundheit verwenden.context.boss.healthist ein schreibgeschützter Snapshot. Um den Boss zu heilen, verwenden Siecontext.boss:restore_health(amount). -
api_version = 1vergessen. Die zurückgegebene Tabelle muss dieses Feld enthalten, oder EliteMobs wird die Fähigkeit nicht laden.
Nächste Schritte
- Erste Schritte -- Dateistruktur, Hooks, erste Power-Erklärung
- Hooks & Lebenszyklus -- vollständige Hook- und Kontextreferenz
- Boss & Entitäten -- Boss-, Spieler- und Entitätsmethoden
- Welt & Umgebung -- Partikel, Sounds, Blitze, Blöcke
- Zonen & Zielerfassung -- native Zonen und Script-Utility-Zonen
- Enums -- gültige Werte für Particle, Sound, Material und andere String-Konstanten
- Fehlerbehebung -- häufige Probleme, Debugging-Tipps und Migrationshinweise
