[Guide] Lua und Addons

Addons, Interface & Makros
[Guide] Lua und Addons - INHALT




Letzte Aktualisierung: 01.02.2012 - WoW 4.3.2
I. Über den Guide

Letzte Aktualisierung: 24.07.2012 (den Ausgangsbeitrag kann/will ich nicht editieren, da sonst die klickbaren Links zu den einzelnen Kapiteln zerstört werden)

Erweiterung / Aktualisierung ist momentan nicht geplant, mir fehlt einfach die Zeit (und auch Muße..) -2012/12/22
II. Addons

Addons für World of Warcraft gibt es in den verschiedensten Formen: vom kleinen Helferlein für einzelne, spezielle Aufgaben über mittlere und große Modifikationen, bis hin zu wahren Mammutprojekten und kompletten UI-Replacements, an denen oft ganze Teams arbeiten.

Für diesen Guide fangen wir allerdings ganz bescheiden an, mit dem minimal notwenigen Grundgerüst, das sich in jedem Addon wiederfindet.

1. "Ich will ein eigenes Addon! ...aber wohin damit?"

In eurem WoW-Ordner (z.B. C:\Programme\World of Warcraft) befindet sich ein Unterordner Interface, darunter ein Ordner AddOns (..\World of Warcraft\Interface\AddOns).

Wenn ihr selbst Addons installiert habt, solltet ihr den Ordner sowieso kennen, und für jedes Addon einen Unterordner sehen können. Für euer eigenes Addon ist das genau der Punkt mit dem wir beginnen: Erstellt einen Ordner mit dem Namen eures Addons. (z.B. ..\World of Warcraft\Interface\AddOns\MeinAddon)
Wichtig: Der Name, den ihr hier vergebt
- sollte einzigartig sein (Sonst würdet ihr andere Addons mit dem selben Namen überschreiben)
- muss nicht der Name sein, unter dem euer Addon im Spiel/auf Addonseiten auftaucht (
- sollte aber trotzdem als euer Addon erkennbar sein


2. "Da kommt es hin! ...aber WAS kommt da hin?"

Alle Addons bestehen aus mindestens zwei Dateien. Die erste der beiden ist eine .toc (Table of Contents - Inhaltsverzeichnis). Diese Datei wird direkt beim Start von WoW geladen, damit Änderungen in dieser Datei wirksam werden, muss WoW also komplett neu gestartet werden.

In dieser Datei werden in erster Linie Informationen über das Addon hinterlegt. Die Einträge richten sich alle nach dem selben Muster:## Schlüssel: Wert
Hier ein paar der wichtigsten und gebräuchlichsten:
## Interface: 40300
Gibt die Version von WoW an, mit der euer Addon kompatibel ist. Dieser Wert richtet sich nach der Versionsnummer des aktuellen Inhaltspatches (z.B. 40300 für 4.3). Kleinere Updates ändern diesen Wert normalerweise nicht (aktuell ist es bspw. immer noch 40300).
## Title: MyAddon
Der Name eures Addons, unter dem es in der Addonliste angezeigt wird. Ihr könnt für verschiedene Sprachversionen unterschiedliche Titel angeben, indem ihr weitere Titeleinträge mit dem jeweiligen Lokalisatonskürzel (deDE (deutsch), enUS (englisch), enGB (englisch, identisch mit enUS), esES (spanisch/Spanien), esMX (Spanisch/Mexiko, so weit ich weiß identisch mit esES). frFR (französich), itIT (italienisch), zhCN (vereinf. chinesisch/China), zhTW (trad. chiniesich/Taiwan), koKR (koreanisch), ptBR (portugiesisch/Brasilien, auch für Portugal genutzt) hinzufügt (z.B. ##Title-deDE: MeinAddon). Der Eintrag ohne Kürzel wird für alle Sprachen, für die nichts angegeben ist, verwendet.
## Notes: Das ist mein Addon, und es kann noch nichts...
Eine kurze(!) Beschreibung eures Addons, sichtbar z.B. in der Addonliste. Kann ebenfalls in unterschiedlichen Sprachen angegeben werden.
## Author: Sará
Hier könnt ihr euren Namen verewigen.
## Version: 1.0
Eine Versionsnummer. Theoretisch kann man alles mögliche als Versionsnummer angeben, aber für bestmögliche Kompatibilität mit automatischen Addon-Updatern empfehlen sich Zahlen. Ob ihr dabei eine "klassische" Versionsnummerierung wählt, die ersten Zahlen dem aktuellen Patch anpasst, oder einfach hochzählt ist ganz euch überlassen.

Wie schon erwähnt handelt es sich hierbei um eine .toc-Datei, d.h. wir müssen sie auch als eine solche speichern (und nicht einfach als .txt). Hier als Beispiel die Vorgehensweise in Notepad/Editor:
- Datei -> Speichern unter
- Speicherort: Im Ordner eures Addons, z.B. C:\Programme\World of Warcraft\Interface\Addons\MeinAddon\
- Dateiname: MeinAddon.toc (Der Name MUSS identisch mit dem Ordnernamen eures Addons sein!)
- Dateityp: Alle Dateien (damit es nicht als .txt, sondern wie beim Dateinamen angegeben als .toc gespeichert wird)
- Codierung: UTF-8 (So könnt ihr problemlos Umlaute verwenden)

3. "Und die zweite Datei?"

Die zweite Datei ist eine .lua-Datei, in die später der eigentliche Code kommt. Diese Datei könnt ihr nennen wie ihr wollt, es ist gängige Praxis, aber keineswegs Pflicht, hier ebenfalls den Namen des Addonverzeichnis zu wählen.

Um den Inhalt kümmern wir uns in den folgenden Kapiteln, erstmal speichern wir die leere Datei ab (Vorgehensweise siehe oben, nur mit .lua statt .toc)

4. "Fertig!"

Fast. Als letzten Schritt müsst ihr nun noch einmal die .toc öffnen, die dann z.B. so aussehen könnte:## Interface: 40300
## Title: MyAddon
## Title-deDE: MeinAddon
## Notes: My own Addon!
## Notes-deDE: Mein eigenes Addon!
## Author: Sará.FdS-EU
## Version: 1

MeinAddon.lua

Ihr habt es wahrscheinlich schon gesehen: In der letzten Zeile fügt ihr den Namen der gerade erstellten .lua-Datei ein. Damit weist ihr WoW an, diese Datei zu laden, wenn euer Addon geladen wird.

Damit ist das Grundgerüst fertig und wir können uns um den Inhalt der .lua kümmern.
III. Lua

World of Warcraft verwendet die Scriptsprache Lua in der Version 5.1, in der das Standard-Benutzerinterface, wie auch sämtliche Addons geschrieben sind. Da der Funktionsumfang entsprechend mächtig ist, unterliegt benutzererstellter Code Einschränkungen: Bestimmte Funktionen (unter anderem zum Bewegen, Benutzen von Gegenständen und Zaubern, Anvisieren von Einheiten, u.Ä.) sind geschützt und können nur vom Originalinterface oder unter besonderen Bedingungen ausgeführt werden.

Davon abgesehen eröffnet die Lua-Schnittstelle Makros und Addons enorme Möglichkeiten, die weit über die des normalen Makrosystems hinaus gehen und in ihrer Gänze den Rahmen dieses Guides sprengen würden, darum beschränke ich mich hier auf die Grundlagen und einige häufig genutzte Funktionen, um die Arbeitsweise zu demonstrieren.

Hilfe zu spezielleren Themen könnt ihr häufig bei den Links im Bereich Nachschlagewerke und Tutorials ( http://eu.battle.net/wow/de/forum/topic/3161500667?page=1#15 ) finden, ansonsten einfach direkt hier im Forum anfragen ;)
III.A Grundlagen

1. Variablen

Bevor wir mit etwas praktischem Anfangen, müssen wir ein bisschen trockene Theorie abarbeiten. Lua arbeitet fast ausschließlich mit Variablen. In Variablen werden sämtliche Arten von Daten gespeichert, von Zahlen und Text bis hin zu kompletten Funktionen.

Variablen werden über ihren Namen angesprochen, dieser kann aus Buchstaben, Zahlen und Unterstrichen bestehen, darf aber nicht mit einer Zahl beginnen. Er kann keine Leerzeichen beinhalten und unterscheidet zwischen Groß- und Kleinschreibung. Theoretisch habt ihr bei der Namenswahl eurer Variablen komplette Freiheit, warum das allerdings keine gute Idee ist, und worauf man achten sollte, dazu später mehr.

Davon abgesehen sind noch einige Worte für Lua-eigene Funktionalitäten reserviert und können nicht als Variablenname benutzt werden:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
while
http://www.lua.org/pil/1.3.html
Was diese Worte bedeuten und wofür sie benutzt werden lernt ihr im Laufe des Guides auch noch, keine Angst. ;)


2. Lua in Makros

Ob ihr Lua in Addons oder Makros nutzt macht in Grunde erstmal keinen Unterschied. Ein paar Sachen sind aber zu beachten:
  • Lua-Code in Makros wird immer mit /run (oder /script, ist aber länger) eingeleitet.
  • Der Code muss in einer Zeile stehen. Wenn ihr eine neue Zeile beginnt (zum Beispiel weil ihr zwischendrin einen normalen Makrobefehl benutzen wollt) muss diese wieder mit /run beginnen.

Darüber hinaus gibt es noch ein paar Einschränkungen:
  • Für euren Code stehen euch nur 250 Zeichen zur Verfügung (Makro-Zeichenbegrenzung von 255 Zeichen minus 5 Zeichen für /run und ein Leerzeichen)
  • Makros alleine können den Inhalt von Variablen beim Aus- oder Umloggen nicht speichern
  • Auf lokale Variablen die in Makros erstellt werden, kann (fast?) nicht wieder zugegriffen werden.

Ansonsten habt ihr genau die gleichen Möglicheiten und könnt sogar die Funktionalität kompletter Addons in Makros nachbauen - wenn der Platz reicht.


3. Code ausführen

Um zu sehen, was der Code, den ihr im Laufe des Guides (kennen)lernt macht, müsst ihr ihn natürlich ausführen. Dazu habt ihr zwei Möglichkeiten, die im folgenden Schritt für Schritt erläutert werden.

Als Addon ausführen:
  • öffnet die .lua eures in Kapitel II. erstellten Addons
  • schreibt den Code hinein
  • speichert die Änderungen an der Datei (Strg-S)
  • ladet im Spiel euer UI neu (/reload)
  • Als Makro ausführen:
  • öffnet den Chat
  • schreibt /run gefolgt vom Code hinein
  • drückt Enter
  • Die Makrovariante eignet sich dabei vor allem für einzelne Befehle oder kurze Funktionen zur Demonstration, für die es sich nicht lohnt das UI neu zu laden; für längeres und komplexeres ist die Addon-Variante zu bevorzugen.


    4. Fertig!

    Jetzt wo ihr wisst, wie ihr euch den Effekt eures Code anschauen könnt, beginnen wir doch mal mit den ersten Beispielen!
    III.B Erste Schritte

    Beginnen wir damit, mit ein paar simplen Makros die Grundlagen kennenzulernen.


    1. Textausgabe (Funktionen und Strings)

    print("Hallo Welt!")Wenn ihr es ausprobiert, seht ihr, dass Hallo Welt! in euer Chatfenster geschrieben wird. Wie das funktioniert? Schauen wir uns die 3 einzelnen Teile und ihr Zusammenspiel mal einzeln an:

    print
    Zuerst einmal ist print nichts weiter als eine Variable. Aber wie der Name schon vermuten lässt, ist in dieser Variablen eine Funktion gespeichert, deren Aufgabe es ist, Text in euer Chatfenster zu schreiben.

    print()
    Die Klammern direkt hinter einem Variablennamen geben an, dass die Funktion, die in dieser Variablen gespeichert ist, ausgeführt werden soll.

    "Hallo Welt!"
    Eine Zeichenfolge, die sich zwischen zwei Anführungszeichen befindet, nennt man String. Strings werden vor allem für Text in jeglicher Form, egal ob Eingabe oder Ausgabe genutzt.

    print("Hallo Welt!")
    Wenn in den Klammern, die angeben, dass eine Funktion ausgeführt werden soll, ein Wert oder eine Variable steht, wird diese der Funktion übergeben. In diesem Fall teilt man der Funktion print auf diese Weise mit, dass sie den String "Hallo Welt" ausgeben soll


    2. Textausgabe 2 (Funktionen mit Rückgabewerten, Variablenzuweisung, Verkettung)

    name = UnitName("player")
    print("Hi, ich bin " .. name .. "!")
    Nicht alle Funktionen haben eine so direkt sichtbare Wirkung wie print(), ein großer Teil der Funktionen, die euch WoW zur Verfügung stellt, dient der Abfrage von Informationen. So liefert beispielsweise UnitName() den Namen einer Einheit zurück. Um der Funktion mitzuteilen, wessen Namen wir wissen möchten, übergeben wir ihr eine sogenannte UnitID, in diesem Fall "player", die UnitID für euren Character.

    Um den Wert, den eine Funktion zurückgibt in einer Variablen zu speichern, wird ein einfaches Gleichheitszeichen benutzt. Die Variable, der ein neuer Wert zugewiesen wird, steht dabei links - der Wert, der ihr zugewiesen wird, rechts vom Gleichheitszeichen.

    Um den so erhaltenen Namen (ein String) in einen Text einzusetzen, werden die Strings miteinander verkettet, also aneinandergehängt. Dies geschieht mittels zwei Punkten ..
    II.C Grundlagen: Funktionen mit Rückgabewerten

    Während SendChatMessage und viele andere Funktionen direkt etwas bewirken, gibt es auch eine vielzahl an Funktionen, die einfach nur Werte zurückgeben. Diese können auf verschiedene Arten genutzt werden.


    Die Funktion UnitName gibt den Namen einer Einheit zurück. Dieser lässt sich zum Beispiel direkt in einen Text einbinden:
    /run SendChatMessage("Hallo "..UnitName("target"))Die beiden Punkte .. sind in Lua der Befehl um zwei Strings (hier "Hallo " und "Name") zu verknüpfen (hier zu "Hallo Name").
    Dieses Beispiel lässt sich natürlich beliebig erweitern:
    /run SendChatMessage("Hallo "..UnitName("target")..", ich bin "..UnitName("player")..", Stufe "..UnitLevel("player").." "..UnitRace("player").."-"..UnitClass("player")..".")
    Zahlenwerte werden automatisch in einen String umgewandelt, wenn sie als Text ausgegeben werden sollen.

    Werden Zahlenwerte zurückgegeben, kann damit natürlich auch gerechnet werden:
    /run SendChatMessage("braucht noch "..(UnitXPMax()-UnitXP()).." Erfahrungspunkte bis zum nächsten Level!","emote")

    Oder man speichert den Rückgabewert einfach in einer Variablen:
    /run Ich = UnitName("player")


    Gibt es mehrere Rückgabewerte, hat man verschiedene Möglichkeiten:
    Alle Werte in Variablen speichern, z.B.:
    /run name, realm = UnitName("target")
    oder sich den gewünschten Wert über die Funktion select herauspicken. select benötigt 2 Argumente, den wievielten Rückgabewert es liefern soll (hier den 2.) und die Funktion, die die Werte liefert (hier UnitName):
    /run realm = select(2,UnitName("target"))
    II.E [wip]
    - reserviert -

    II.B Grundlagen: Funktionen (Bsp: SendChatMessage)

    Funktionen sind quasi die Slashbefehle in Lua, ohne sie passiert (fast) nichts. Als Beispiel wird eine der am häufigsten in Makros genutzten Funktionen dienen: SendChatMessage.
    Wie in III.A beschrieben ist SendChatMessage erstmal nichts als eine Variable, in der eine Funktion gespeichert ist. Um die dort gespeicherte Funktion auszuführen, hängen wir Klammern an den Namen der Variablen:
    /run SendChatMessage()
    Prompt bekommen wir eine Fehlermeldung, die uns mitteilt, wie die Funktion zu benutzen ist:
    Usage: SendChatMessage(text [,type] [,language] [,targetPlayer])

    Wir können der Funktion also 4 Werte ("Argumente") mitgeben, von denen 3 optional sind (in eckigen Klammern). Da dies allein noch nicht alle Fragen klärt, bzw viele Funktionen überhaupt keine Rückmeldung geben, wie sie zu benutzen sind, werfen wir einen Blick auf eine der Eingangs schon erwähnten Seiten: http://wowprogramming.com/docs/api/SendChatMessage
    SendChatMessage("text" [, "chatType" [, "language" [, "channel"]]])
    Arguments:
    • text - Message to be sent (up to 255 characters) (string)
    • chatType - Channel on which to send the message (defaults to SAY if omitted) (string)
    • language - Language in which to send the message; defaults to Common (for Alliance players) or Orcish (for Horde players) if omitted (string)
    • channel - If chatType is WHISPER, name of the target character; if chatType is CHANNEL, number identifying the target channel; ignored otherwise (string)


    Das erste Argument, text, ist ziemlich logisch: ein string mit maximal 255 Zeichen, der die zu versendende Nachricht enthält.
    /run SendChatMessage("Hallo Welt!")
    Um einen anderen Chat als /s benutzen wollen, wird sein Typ (ebenfalls als string) als zweites Argument, getrennt durch ein Komma, angeben.
    /run SendChatMessage("Hallo Welt!","YELL")
    Als 3. Argument kann eine Sprache angegeben werden (bei euch zum testen entsprechend anpassen, die Namen eurer Sprachen findet ihr im Zauberbuch unter Allgemein -> Sprachen).
    /run SendChatMessage("Hallo Blutelfen!","YELL","Thalassisch")
    Das 4. Argument gibt nun die Channelnummer (wenn das 2. Argument "CHANNEL" war) oder den Namen des Empfängers (wenn das 2. Argument "WHISPER" war) an. Wenn man dabei keine bestimmte Sprache angeben will, kann man das 3. Argument nicht einfach weglassen, man gibt stattdessen nil, also "nichts", "kein Wert", an.
    /run SendChatMessage("Hallo Welt!","CHANNEL",nil,"2")
    Perfekt!
    --- Hier könnte Ihre Werbung stehen! ---

    II.C Grundlagen: Funktionen mit Rückgabewerten

    Während SendChatMessage und viele andere Funktionen direkt etwas bewirken, gibt es auch eine vielzahl an Funktionen, die einfach nur Werte zurückgeben. Diese können auf verschiedene Arten genutzt werden.


    Die Funktion UnitName gibt den Namen einer Einheit zurück. Dieser lässt sich zum Beispiel direkt in einen Text einbinden:
    /run SendChatMessage("Hallo "..UnitName("target"))Die beiden Punkte .. sind in Lua der Befehl um zwei Strings (hier "Hallo " und "Name") zu verknüpfen (hier zu "Hallo Name").
    Dieses Beispiel lässt sich natürlich beliebig erweitern:
    /run SendChatMessage("Hallo "..UnitName("target")..", ich bin "..UnitName("player")..", Stufe "..UnitLevel("player").." "..UnitRace("player").."-"..UnitClass("player")..".")
    Zahlenwerte werden automatisch in einen String umgewandelt, wenn sie als Text ausgegeben werden sollen.

    Werden Zahlenwerte zurückgegeben, kann damit natürlich auch gerechnet werden:
    /run SendChatMessage("braucht noch "..(UnitXPMax()-UnitXP()).." Erfahrungspunkte bis zum nächsten Level!","emote")

    Oder man speichert den Rückgabewert einfach in einer Variablen:
    /run Ich = UnitName("player")


    Gibt es mehrere Rückgabewerte, hat man verschiedene Möglichkeiten:
    Alle Werte in Variablen speichern, z.B.:
    /run name, realm = UnitName("target")
    oder sich den gewünschten Wert über die Funktion select herauspicken. select benötigt 2 Argumente, den wievielten Rückgabewert es liefern soll (hier den 2.) und die Funktion, die die Werte liefert (hier UnitName):
    /run realm = select(2,UnitName("target"))
    Re
    ser
    vi
    ert
    IV. Tutorials und Nachschlagewerke

    Deutsch:

    Englisch:

    Funktionsübersicht (API)
  • http://wowprogramming.com/docs/api
  • http://www.wowpedia.org/World_of_Warcraft_API
  • IV. Praxisbeispiele:

    /run n=0 for i=0,NUM_BAG_SLOTS do for j=1,GetContainerNumSlots(i) do if GetContainerItemInfo(i,j) then n=n+select(2,GetContainerItemInfo(i,j)) end end end print(n)Zählt alle Items in euren Taschen zusammen.

    --

    Handelschannel per Whisper an einen anderen Char weiterreichen:/run TCW=TCW or CreateFrame"FRAME"TCW:RegisterEvent"CHAT_MSG_CHANNEL"TCW:SetScript("OnEvent",function(_,_,m,n,_,c)SendChatMessage("["..c.."] ["..n.."]: "..m,"WHISPER",nil,"NAMEDEINESMAINS")end)Namen des Mains eintragen und fertig. Zum stoppen:/run TCW:SetScript("OnEvent",nil)oder einfach /reload
    Praxisprojekt: MyMountspecial

    Auf Anregung von Lethyssa hin werde ich hier anhand von MyMountspecial (Idee von Alrik, http://eu.battle.net/wow/de/forum/topic/5300140321 ) die Entstehung und technische Umsetzung eines kleinen Addons dokumentieren.
    Source, bzw. fertiges Addon gibt es unter http://sarafds.de/addons

    1. Grundidee
    Ein Addon, das in zufälligen Zeitabständen /mountspecial auslöst - das war die Idee. Realisieren lässt sich das Ganze, da es sich bei /mountspecial um ein Emote handelt, das sich in der WoW-API durch DoEmote() aufrufen lässt.

    2. .toc
    ## Interface: 50001
    ## Title: MyMountspecial
    ## Notes: Lets your mount randomly perform its /mountspecial

    ## Author: Sará.Festung der Stürme (EU-de)
    ## Version: 1

    ## SavedVariable: MyMountspecial_SV

    MyMountspecial.lua
    Nichts besonderes hier, 50001 für die aktuelle WoW-Version 5.0.5, MyMountspecial_SV als SavedVariable in der Werte dauerhaft gespeichert werden können.

    3. Lua - Basics
    In einem bestimmten Intervall eine Funktion auszuführen schreit nach einem OnUpdate-Script. Dieses wird bei jedem neuen Bild ausgeführt, bei einer Bildwiederholrate von 30fps also z.B. 30 mal in der Sekunde. Dafür ist zu allererst ein Frame, dem dieses Script zugewiesen wird, von Nöten.local f = CreateFrame("frame");
    Dazu nun das OnUpdate-Script. :SetScript() weist dem Frame f ein Script zu. Der erste Wert gibt den Script-Typ an, hier also "OnUpdate". Der zweite Wert ist die Funktion, die beim Aufruf ausgeführt wird. Der Funktion eines OnUpdate-Scripts werden 2 Werte übergeben, der Erste ist eine Referenz auf den Frame (für uns egal), der Zweite ist die Zeit, die seit dem letzten Aufruf vergangen ist.f:SetScript("OnUpdate",function(s,e)
    end);

    Diese Funktion wird nun mit Leben gefüllt. Zuerst brauchen wir eine Variable, in der die vergangene Zeit gespeichert wird. Diese müssen wir außerhalb des Scripts definieren - ansonsten würde sie bei jedem Script-Aufruf wieder auf ihren Standardwert zurückgesetzt. In der Funktion wird nun bei jedem Update die Zeit seit dem letzten Update hinzuaddiert.local timer = 0;
    f:SetScript("OnUpdate",function(s,e)
    timer = timer + e;
    end);

    Als letzter Schritt fehlt nun noch eine Abfrage, ob die gewünschte Zeit (nehmen wir mal 3 Minuten = 180 Sekunden) vergangen ist. Wenn ja, führen wir das gewünschte Emote aus und setzen den Zeit-Zähler zurück auf 0.local f = CreateFrame("frame");
    local timer = 0;
    f:SetScript("OnUpdate",function(s,e)
    timer = timer + e;
    if (timer > 180) then
    DoEmote("MOUNTSPECIAL");
    timer = 0;
    end;
    end);

    Nun fehlt nur noch eine Sache, bis man sagen könnte "Es ist fertig!": Das Intervall zufällig wählen. Dazu setzen wir die Zeit, die vergehen soll, nicht fest ein, sondern als Variable. Den Inhalt der Variablen lassen wir über random() zufällig bestimmen - sowohl beim Erstellen, als auch sobald das Emote ausgeführt wurde, damit der Zeitraum jedes Mal unterschiedlich ist.local f = CreateFrame("frame");
    local timer = 0;
    local interval = random(120,180);
    f:SetScript("OnUpdate",function(s,e)
    timer = timer + e;
    if (timer > interval) then
    DoEmote("MOUNTSPECIAL");
    interval = random(120,180);
    timer = 0;
    end;
    end);
    Fertig!

    4. Lua - Advanced
    Ihr seid noch da? Sehr gut! In der Form oben funktioniert es natürlich schon, aber sieht doch eher nach einem besseren Makro aus: Kümmern wir uns nun also um die Punkte, die von einem Addon erwartet werden: Einstellungen speichern und im Spiel ändern.

    Wer sich noch an den Eintrag in der .toc erinnert weiß, dass die Variable MyMountspecial_SV zum Speichern der Daten eingeplant war. Die Einstellungsmöglichkeiten sind überschaubar:
    - Addon komplett Ein- und Ausschalten
    - festes Intervall festlegen
    - Umschalten zwischen festem und zufälligem Intervall
    - Minimal- und Maximalwert für das zufällige Intervall festlegen

    Zu diesem Zweck speichern wir 5 Variablen in einer Tabelle:MyMountspecial_SV = {
    enabled = true,
    interval = 180,
    randominterval = true,
    mininterval = 120,
    maxinterval = 240
    }

    Um eventuell schon in der Variable gespeicherte Werte nicht zu überschreiben, bedienen wir uns dieses Konstruktes:MyMountspecial_SV = MyMountspecial_SV or {..Standardwerte..}or funktioniert im Grunde wie ein if..then..else-Konstrukt. Hier sähe es so aus, da ist es mit or doch viel schöner, oder?if (MyMountspecial_SV) then
    MyMountspecial_SV = MyMountspecial_SV;
    else
    MyMountspecial_SV = {..Standardwerte..}
    end

    and und or werten den Wert vor ihnen als falsch (false und nil) oder wahr (alles andere, auch leere Strings "" und 0) aus. or gibt den Wert vor sich aus, wenn er wahr ist, sonst den Wert hinter sich ("Dieser Wert wenn er wahr ist ODER sonst den dahinter"). Bei and ist es anders herum: Ist der Wert davor wahr, gibt es den Wert dahinter aus. Ist der Wert davor falsch, gibt es diesen aus.

    Als Tabelle wird es vielleicht ein bisschen deutlicher:(a or b) | b true | b false
    ---------+--------+---------
    a true | a | a
    a false | b | b

    (a and b)| b true | b false
    ---------+--------+---------
    a true | b | b
    a false | a | a
    Anfangs wirkt es verwirrend (tief genug verschachtelt auch später noch), aber sobald man es verstanden hat ist es sehr praktische Werkzeuge, die man nicht mehr missen mag.

    Fügen wir nun noch die Variablen aus der SV in den Code ein, sieht der aktuelle Stand so aus:MyMountspecial_SV = MyMountspecial_SV or {
    enabled = true,
    randominterval = true,
    interval = 180,
    mininterval = 120,
    maxinterval = 240
    }

    local f = CreateFrame("frame");
    local timer = 0;
    local interval = random(MyMountspecial_SV.mininterval, MyMountspecial_SV.maxinterval);
    f:SetScript("OnUpdate",function(s,e)
    timer = timer + e;
    if (timer > interval) then
    DoEmote("MOUNTSPECIAL");
    interval = random(MyMountspecial_SV.mininterval, MyMountspecial_SV.maxinterval);
    timer = 0;
    end;
    end);


    Um sicherzugehen, dass die gespeicherten Daten auch schon geladen sind, wenn wir den Inhalt der Optionstabelle festlegen, nutzen wir eine weitere - die wohl wichtigste - Script-Art: OnEvent.

    Das WoW-Interface ist größtenteils Event-basiert. Damit das UI weiß, wann etwas passiert können sich Frames für verschiedene Events registrieren. Unser gewünschtes Event ist "ADDON_LOADED", das dann gesendet wird, wenn sämtliche Dateien - inklusive der SavedVariables - eines Addons geladen wurden.f:RegisterEvent("ADDON_LOADED");
    Wie bei OnUpdate weisen wir unserem Frame das Script zu und geben eine Funktion an, die aufgerufen wird, wenn unserem Frame ein Event gesendet wird. Wie bei OnUpdate werden dieser Funktion 2 Werte übergeben, der Erste ist wieder eine Referenz auf den Frame, der Zweite ist hier der Name des Events. Das alleine wäre ja langweilig, darum können bei Events auch noch weitere Werte übergeben werden. ADDON_LOADED zum Beispiel übergibt als dritten Wert den Namen des Addons, das gerade geladen wurde.

    Das Event brauchen wir nicht zu prüfen - unser Frame ist ja nur für ein Event registriert, wenn ein Event bei uns ankommt ist es also eh immer ADDON_LOADED. Dafür prüfen wir jedes Mal, ob es sich beim gerade geladenen Addon um unseres handelt. Wenn ja, hören wir auf, weitere ADDON_LOADED-Events zu empfangen - wir haben ja was wir wollten - und legen den Inhalt unserer Optionen fest. Hier habe ich das Erstellen der Optionstabelle in eine eigene Funktion ausgelagert, damit man sie auch noch an anderer Stelle (z.B. zum Zurücksetzen auf Standardeinstellungen) benutzen kann. Man könnte es aber auch direkt in die OnEvent-Funktion schreiben.local function Initialize()
    MyMountspecial_SV = MyMountspecial_SV or {
    enabled = true,
    randominterval = true,
    interval = 180,
    mininterval = 120,
    maxinterval = 240
    }
    end
    f:SetScript("OnEvent",function(s,e,a)
    if (a == "MyMountspecial") then
    f:UnregisterEvent("ADDON_LOADED");
    Initialize();
    end;
    end);
    Das muss reichen.


    *legt blaue Farbe, eine Pinnadel und einige andere Utensilien bereit*

    "Soo.. Nüsschen, Schwertpolitur, ein paar Geisterperlen der Winterfelle, ein leckerer Opfergnom und etwas Glitzerblubberstaub. Mal sehen, wen es zuerst anlockt!"
    Super. *daumen hoch* Dann ist ja wieder Platz zum Fragen stellen.
    hey, wird der giude noch erweitert? wäre echt super!
    Wunsch: Am anfang den Grundaufbau von Addons zeigen, also *.toc, *.lua und *.xml dateien und inhalt denn nur mit den befehlen bekommt man kein addon zum laufen ^^
    Wunsch2: ein kleines Beispiel addon am ende jedes abschnittes damit man sieht wie die befehle "fertig" funktionieren

    Nimm an der Unterhaltung teil!

    Zurück zum Forum