← GeekServers.gg [DEBUG:1782456837]
⚠ Archive en lecture seule — Retour sur GeekServers.gg
Tutoriel sur les addons


    J'ai toujours voulu faire un tuto sur les addons, donc je vais essayer de dire tout ce que je connais, je sais bien que très peu de joueurs liront tout ça, et encore moins feront des addons après avoir lut ces posts, mais c'est pas grave.

    Il y a toujours des joueurs qui cherchent comment faire des addons, au moins ils en auront la possibilité s'ils trouvent ce post, et ne seront pas bloqués comme j'ai été il y a longtemps.

    Ce tuto va être très long, genre c'est la première fois que je découpe autant un post ^^ :

    Image

    Il est susceptible d'évoluer clairement, car je ne connais pas tout dans les addons, il y aura peut être des erreurs qui seront corrigés par d'autres joueurs, et mon premier jet ne va pas totalement vous accompagnez dans la création d'addon, il y aura pas mal de chose que vous devrez voir par vous même, mais je repasserai sur les posts pour les compléter, et surtout pour les fautes d'orthographes ^^.

Sommaire

  1. C'est quoi un addon ?
  2. Pourquoi faire un addon ?
  3. Comment faire un addon ?
  4. L'ordre de parcourt du fil d'exécution
  5. Faire du visuel
  6. Les objets qui existent pour faire vos addons
  7. Les scripts handlers attachés aux objets
  8. Les hooks
  9. Les événements
  10. Les SaveVariables
  11. Le LUA dans wow
Marodheure

<Hell Script>


C'est quoi un addon ?


    Dans wow un addon est un outil qui permet de faire évoluer l'interface et le gameplay, positivement si l'addon est bien fait, et négativement s'il ne sert à rien.

    Voila un exemple d'interface avec des addons :

    Image

    Ces modifications d'interface avec des addons sont sans doute motivés par l'envie d'avoir une belle interface, c'est aussi une bonne raison pour certains.

    Faites toute fois attention à la totalité de la surface de votre écran recouvert par des addons, car ils vous cachent l'environnement.
Marodheure

<Hell Script>


Pourquoi faire un addon ?


    Ça, c'est à vous de le savoir, mais je vais vous donner quelques exemple.

    1. Avoir une belle interface.
    2. Avoir une interface plus compact, donc plus optimisé, et donc plus belle pour certains.
    3. En rogue, afficher les points de combos de manière plus visible.
    4. En rogue, voir les ticks d'énergies.
    5. Voir beaucoup mieux visuellement les procs de certains sorts, en war la fulgurance.
    6. Voir la menace.
    7. Voir les dégats.

    etc...

    Vous trouverez votre raison assez facilement. Une fois que vous avez votre raison, vous devez savoir si elle n'est pas faisable facilement en macro, en générale la réponse est non. J'explique ici la différence les macros et les scripts, sachant qu'un addon est composé de scripts, c'est comme si je comparais les macros et les addons (dans le post linké).
Marodheure

<Hell Script>


Comment faire un addon ?


  1. Où se renseigner ?

      Tout ce que je vais écrire, vient de ces sites :

      1. wow wiki
      2. wowprogramming
      3. wow gamepedia

      Le site 3 wow gamepedia, est pratiquement pareil que le site 1 wow wiki, donc je n'ai utilisé que les deux premiers sites pour tout apprendre. Et ces sites sont toujours maintenus à la dernière version du jeu, mais on est à vanilla, donc il y aura plein d'outils qu'on ne pourra pas utiliser, et ce n'est pas toujours préciser.
  2. Commencer à coder

      Je précise que j'explique les addons sous windows, sur mac les addons fait sous windows ne fonctionne pas toujours, je ne sais pas trop pourquoi, dans certains addons il y a parfois un dossier MACOSX, ou un truc dans le genre, c'est peut être lié.

      Pour commencer, vous devez avoir le jeu fermé, vous allez dans votre dossier d'installation du jeu, et ensuite dans le dossier Interface/Addons/, là vous créez un nouveau dossier appelé MonAddon, tous les noms d'addons doivent être unique, sinon il y aura un conflit, vous allez dans ce nouveau dossier et vous créez trois fichiers :

        MonAddon.toc
        MonAddon.xml
        MonAddon.lua

      toc, xml et lua sont les extensions des fichiers, vous devez pouvoir modifier les extensions sur windows, par défaut ont ne peux pas.

      Ça vous donne ça :

      Image

      Le fichier MonAddon.toc doit avoir le même nom que le dossier de l'addon, les autres fichiers peuvent être appelé autrement on va voir pourquoi.
  3. Le fichier .toc

      Description du fichier .toc par wow wiki ici.

      Voila un exemple de fichier .toc très simple pour notre addon :

      Image

      Code: Tout sélectionner
      ## Interface: 11200
      ## Title: MonAddon
      MonAddon.lua
      MonAddon.xml


      Le plus petit addon fonctionnel peut être fait avec deux fichiers, un .toc et un .lua, mais pas la peine de voir ça pour le moment étant donné que vous verrez souvent des fichiers .xml dans les dossiers des autres addons, mais sachez a titre d'information que c'est possible.

      Le début des lignes :

        Une ligne commençant par 1 dièse # est un commentaire, ce qu'il y a d'écrit après le # n'a pas d'incidence, sauf les retours à la ligne, car cela créé une nouvelle ligne.

        Une ligne commençant par 2 dièses ## signifie qu'on affecte une valeur à une variable.

        Toutes les lignes ne commençant pas par 1 ou 2 dièses, sont des lignes qui indiquent le chemin d'accès à un fichier qui compose l'addon.

      Description des lignes :

        À la ligne 1, la variable nommée "Interface" aura pour valeur "11200", vous avez besoin de cette ligne pour dire au jeu, pour quelle version du jeu l'addon est codé, 11200 signifie que l'addon est coder pour la 1.12.0, mais ça fonctionne aussi pour la 1.12.1, cette subtilité est décrire ici. C'est cette ligne qui est en cause quand un addon est vu comme périmé par le jeu, quand vous êtes sur votre liste de personnages, en train de regarder la liste d'addons. S'il y a écrit 20403, il s'agit d'un addon bc 2.4.3, écrire 11200 à la place du 20403, va juste retirer "l'alarme" et l'addon sera ne plus considéré périmé par le jeu, mais la composition réelle de votre addon sera toujours en code buring crusade et vous occasionnera des erreurs LUA en jeu.

        À la ligne 2, la variable nommée "Title" aura pour valeur "MonAddon", le nom de l'addon donc, c'est ce qui va être affiché dans la liste des addons, quand vous êtes sur la liste de vos personnages. Cette ligne est obligatoire, car un addon sans titre c'est un peu nul ^^.

        La ligne 3, indique le chemin d'accès vers le fichier MonAddon.lua, la ligne 4 indique le chemin d'accès vers le fichier MonAddon.xml, si vos fichiers sont dans un sous dossier qui s'appel "SousDossier", vous pouvez l'indiquer comme ça SousDossier/MonAddon.xml.

      Le moment de la lecture du fichier par le jeu :

        Ce fichier est lut par le jeu à un certain moment, je ne sais pas quand très précisément, mais je sais à partir de quand on peut être sur que le jeu l'a lu, c'est à dire au moment de la liste de personnage, car vous voyez dans le jeu ce qu'il y a écrit dans votre fichier .toc, à savoir le nom de l'addon, donc le jeu a lu votre fichier .toc, ça signifie que si vous modifiez le fichier .toc d'un addon, vous devez redémarrer le jeu pour que les modifications soit prisent en compte, car le jeu ne le relira pas une seconde fois.

      L'ordonnance des lignes des fichiers :

        L'ordonnance des lignes qui indiquent les chemins d'accès aux fichiers est importante, quand un fichier A a besoin d'un fichier B, le fichier B doit être écrit plus haut que le fichier A, dans notre exemple le fichier MonAddon/xml a besoin du fichier MonAddon.lua, donc le fichier MonAddon.lua est écrit au dessus. Cela dépend de comment vous coder votre addon, de quel fichier à besoin de quel fichier.
  4. Le fichier .xml

      Description du fichier .xml par wow wiki ici.

      Là on va parler de langage xml, si vous ne connaissez rien, faite un copier coller, ça fonctionnera, et on n'en restera là.

      Voial un exemple de fichier .xml pour notre addon :

      Image

      Code: Tout sélectionner
      <Ui   xmlns="http://www.blizzard.com/wow/ui/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.blizzard.com/wow/ui/
         ..\FrameXML\UI.xsd">
         
         <Script file="MonAddon.lua"/>
         
         <Frame name="MonAddon_MainFrame">
            <Scripts>
               <OnLoad>
                  MonAddon_MainFrame_OnLoad()
               </OnLoad>
               <OnUpdate>
                  MonAddon_MainFrame_OnUpdate(arg1)
               </OnUpdate>
               <OnEvent>
                  MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
               </OnEvent>
            </Scripts>
         </Frame>
      </Ui>


      Ici vous devez comprendre un peu le langage xml, mais si jamais vous ne comprenez pas ou comme moi vous détestez tout les langages à balise, ne vous en faites pas on n'y touchera jamais, mais sachez que toute l'interface d'un addon peut être faite en xml, c'est un choix.

      Description des lignes :

        De la ligne 1 à 4, je ne sais franchement pas à quoi ça sert, mais c'est comme ça dans tout les addons.

        La ligne 6 indique que l'on voit appeler des scripts qui se trouvent dans le fichier MonAddon.lua, rappelez vous ce qu'on a dit plus haut, le fichier MonAddon.xml à besoin du fichier MonAddon.lua, et c'est pour cette raison.

        La ligne 8 créée une Frame, qui a pour nom "MonAddon_MainFrame". Le nom de la frame est très important, car il ne doit pas rentrer en conflit avec le nom de d'autre frame de d'autre addon, donc choisissez un nom unique, en général quand on met le nom de l'addon dedans, il y a beaucoup de chance que le nom devienne unique, car on sait à peu prés quel addons on possède, et on ne va clairement pas choisir le nom d'un addon déjà existant.

        La ligne 9 dit que la frame va contenir des scripts.

        La ligne 10 indique qu'on veut affecter un script au chargement de l'addon. Donc en ligne 11 on écrit le nom de la fonction qui sera appelée au chargement de l'addon, on en reparle ^^. Cette fonction ne sera appelé qu'un seule fois au chargement, on en reparle aussi xD.

        La ligne 12 indique qu'on veut affecter un script pour mettre à jour l'addon entre chaque frame du jeu, si votre jeu tourne à 60 fps, votre addon est mit à jour 60 fois par seconde aussi, une fois entre chaque frame. En ligne 14 on écrit le nom de la fonction qui sera appelée entre chaque frame du jeu.

        La ligne 16 indique qu'on veut affecter un script pour recevoir des événements, on en reparle clairement plus loin de ça xD. En ligne 17 on écrit le nom de la fonction qui recevra les événements.

      Les fonctions lignes 11, 14 et 17 :

        On écrit les nom de fonctions à ces lignes pour dire a la frame MonAddon_MainFrame qu'elle devra les appelées au "moment" des scripts OnLoad, OnUpdate et OnEvent, mais elles sont implémentées dans le fichier MonAddon.lua.

      Infos supplémentaires pour la ligne 8 :

        La ligne 8 déclare la création d'une frame, dans l'interface de wow, une frame est le conteneur de base, tout est dans une frame (la minimap, les barres d'action, l'icone d'un sort, votre portrait, l'interface de raid, etc..).

        À chaque fois qu'une frame contient quelques chose de visuel, cette frame aura une position x, y et deux dimensions largeur et hauteur.

        La frame ligne 8 ne contiendra rien de visuel, donc nous avons pas besoin de lui en attribuer, il s'agit juste d'une frame pour "ranger" les scripts de base, c'est un choix d'architecture d'addon, on pourrait clairement faire autrement.
  5. Le fichier .lua

      Voila votre premier fichier lua :

      Image

      Code: Tout sélectionner
      function MonAddon_MainFrame_OnLoad()
      end

      function MonAddon_MainFrame_OnUpdate(arg1)
      end

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
      end


      Vous pouvez voir l'implémentation des trois fonctions appelées par la frame aux moments correspondant au chargement, à la mise à jour et aux réceptions des événements. Leurs corps est vide, donc elles ne font rien.

      C'est ici que tout va ce passer, mais je ne peux pas dire grand chose de plus avant d'autres précision, on y revient après.

    Actuellement cet addon n'affiche rien.
Marodheure

<Hell Script>


L'ordre de parcourt du fil d'exécution


    Pour savoir comment coder un addon, il faut savoir comment wow appels les addons qu'on lui passe.

    Le fil d'exécution :

      C'est un peu abstrait comme notion, mais vitale pour comprendre la programmation.

      Par exemple vous allez dans le dossier d'installation de wow, et vous ouvrez avec le bloc note le fichier WoW.exe (ne le faites pas vous même, je vous met une image)

      WoW.exe ouvert en bloc note :

      Image

      C'est une suite de caractères qui ne veut rien dire pour nous, sauf à certain endroits vous voyez quelques phrases compréhensibles en anglais.

      Le fil d'exécution c'est ce qui parcourt le fichier et le li, caractère par caractère. Pour imager encore un peu plus, le fil d'exécution a une "tête de lecture" qui li le premier caractère puis le second etc..

      On peut imager le parcourt de la tête de lecture du fil d'exécution en sélectionnant le premier caractère :

      Image

      Puis le second :

      Image

      Etc..


      Chaque caractère signifie quelque chose pour l'ordinateur, lui fait utiliser une fonctionnalité, et par exemple, certains caractères disent à la tête de lecture de sauter à un endroit du fichier, donc faire une opération stockée plus loin ou pour répéter une opération en sautant en arrière. D'autres caractères peuvent dire de créer un autre fil d'exécution qui aura lui aussi une tête de lecture à un autre endroit du fichier.

      Bref, dans ce fichier il y a des séries de caractères parcourut par une tête de lecture, qui gère le rendu graphique du jeu et donc les addons,

    Le parcourt du fil d'exécution dans wow et les addons :

    1. Vous ouvrez le jeu, vous arrivez sur la fenêtre de connexion.
    2. Vous écriez vous nom de compte et votre mot de passe.
    3. Vous vous connectez.
    4. Le jeu lit les fichier .toc des addons.
    5. Vous arrivez sur la liste de vos peronnages.
    6. Vous choisissez votre personnage.
    7. Vous commencer le chargement pour entrer dans le jeu :

      1. Pour chaque addon, un par un, le jeu lit tout vos fichier .lua et .xml dans l'ordre d'écrit dans les fichiers .toc.
      2. Plus le nombre d'addon est élevé plus cette phase est longue.
    8. Vous êtes dans le jeu (pour le serveur), mais le jeu n'a pas encore calculer la première image du jeu (sur votre pc).
    9. Le jeu calcule la première image du jeu :

        Là je ne sais pas ce que fait le jeu réellement mais, j'interprète beaucoup.

      1. Le jeu calcul graphiquement l'environnement 3D (le relief, les personnages, l'équipement, les bâtiments etc..) il en fait une image.
      2. Le jeu calcul graphiquement l'interface :

        1. Étant donné qu'on est dans le calcul de la première image du jeu, le jeu appel pour chaque addon, un par un, les scripts OnLoad de chaque frame déclarée dans les fichiers .xml.
        2. Le jeu appel pour chaque addon, un par un, les scripts OnUpdate des frames dans les fichiers .xml.
        3. Une fois que tout les addons ont eu leurs scripts OnUpdate entièrement exécutés, l'interface est entièrement calculée pour l'image actuelle.
      3. Le jeu colle l'image de l'interface sur l'image de l'environnement 3D.
    10. Le jeu affiche la première image.
    11. Le jeu calcul la seconde image :

      1. Le jeu recommence l'étape 9, mais il n'appel plus les scripts OnLoad des frames où il l'a déjà appelé, par contre il appel les scripts OnLoad des frames qui ont étaient crées pendant l'appel des scripts OnLoad et OnUpdate du calcul de l'image précédente (on en reparle ^^).
      2. Le jeu appel les scripts OnUpdate de toutes les frames qui sont déjà chargées, et des nouvelles frames qui ont eu le scripts OnLoad entièrement exécutés durant le calcul de cette nouvelle image.
    12. etc...

    La même chose, mais dans la pratique :

      On reprend notre addon, mais avec quelques modification.

      Voila votre nouveau fichier .xml :

      Image

      Code: Tout sélectionner
      <Ui   xmlns="http://www.blizzard.com/wow/ui/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.blizzard.com/wow/ui/
         ..\FrameXML\UI.xsd">
         
         <Script file="MonAddon.lua"/>
         
         <Frame name="MonAddon_MainFrame">
            <Scripts>
               <OnLoad>
                  ChatFrame1:AddMessage("OnLoad dans le fichier xml.")
                  MonAddon_MainFrame_OnLoad()
               </OnLoad>
               <OnUpdate>
                  ChatFrame2:AddMessage("OnUpdate dans le fichier xml.")
                  MonAddon_MainFrame_OnUpdate(arg1)
               </OnUpdate>
               <OnEvent>
                  ChatFrame1:AddMessage("OnEvent dans le fichier xml.")
                  MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
               </OnEvent>
            </Scripts>
         </Frame>
      </Ui>


      Et votre nouveau fichier .lua :

      Image

      Code: Tout sélectionner
      ChatFrame1:AddMessage("Passage 1 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnLoad()
         ChatFrame1:AddMessage("OnLoad dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 2 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnUpdate(arg1)
         ChatFrame2:AddMessage("OnUpdate dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 3 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
         ChatFrame1:AddMessage("OnEvent dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 4 du file d'exécution dans le fichier lua.")


      Vous pouvez voir des ligne en plus composées de : ChatFrame1:AddMessage("Avec du text ici.")
      Cette ligne va afficher du text dans la première fenêtre de votre t'chat box, et donc : ChatFrame2:AddMessage("Avec du text ici.")
      va l'afficher dans la seconde, à savoir votre journal de combat.

      Voila ce que ça donne en se connectant ou en faisant un ReloadUI :

      Image

      Quand on est programmeur il faut être curieux, en écrivant ce tuto, j'essaye encore des chose que je n'est pas essayé depuis que je code des addons :

      Je change le fichier .toc en :

      Image

      J'ai inversé l'ordre des deux fichiers, car que sait que l'ordre est important et je voulais voir la différence visuelle que cela causerait.

      Et voila ce que ça donne :

      Image

    Conclusion :

      Je vais pas faire une conclusion spécialement étendue car je n'en voit pas l'intérêt, on voit que le fichiers .lua est lut deux fois, dans un ordre différent en fonction de l'ordre dans le fichier .toc, on peut donc comparer avec la théorie que j'ai écrit plus haut et affiner ses propres conclusions, chacun pense ce qu'il veut du fonctionnement interne de wow, avec sa propre rétro-ingénierie. Et clairement je ne sais pas pourquoi le fichier .lua est lu une seconde fois.

      Mais je vais quand même faire un dernier essaie avant de repasser sur le déroulement normal du tuto "faire des addons" car là on est plus dans "comprendre wow".

    Donc la même, avec des visuel plus précis :

      Je remet le fichier .toc comme au début :

      Image

      Code: Tout sélectionner
      ## Interface: 11200
      ## Title: MonAddon
      MonAddon.lua
      MonAddon.xml


      Nouveau fichier .xml :

      Image

      Code: Tout sélectionner
      <Ui   xmlns="http://www.blizzard.com/wow/ui/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.blizzard.com/wow/ui/
         ..\FrameXML\UI.xsd">
         
         <Script file="MonAddon.lua"/>
         
         <Frame name="MonAddon_MainFrame">
            <Scripts>
               <OnLoad>
                  ChatFrame1:AddMessage("OnLoad dans le fichier xml.")
                  MonAddon_MainFrame_OnLoad()
               </OnLoad>
               <OnUpdate>
                  MonAddon_MainFrame_OnUpdate(arg1)
               </OnUpdate>
               <OnEvent>
                  ChatFrame1:AddMessage("OnEvent dans le fichier xml.")
                  MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
               </OnEvent>
            </Scripts>
         </Frame>
      </Ui>


      Nouveau fichier .lua :


      J'ai rajouté du code de la ligne 17 à 20 qui sert à ne passer qu'une fois sur les lignes 18 et 19.
      Et du code de la ligne 22 à 26 qui utilise des variables déclarées en haut du fichier, ce code sert à passer tout les 1 seconde dans les lignes 24 et 25.

      Je ne vais pas détailler comment coder en lua, ça serait trop long, la seule chose que je dois vous dire c'est que dans la fonction MonAddon_MainFrame_OnUpdate l'argument "arg1" a pour valeur un nombre décimale qui vaut le temps écoulé depuis le dernier passage dans la fonction MonAddon_MainFrame_OnUpdate, et avec et un peu de calcul on arrive à "mesurer" le temps écoulé.

      J'ai fais toutes mes images avec une variable Time à la ligne 1 qui ne sert à rien.

      Image

      Code: Tout sélectionner
      local Current = 0
      local Update = 0
      local Refresh = 1

      local FirstUpdate = false

      ChatFrame1:AddMessage("Passage 1 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnLoad()
         ChatFrame1:AddMessage("OnLoad dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 2 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnUpdate(arg1)
         if FirstUpdate == false then
            ChatFrame1:AddMessage("OnUpdate premier passage.")
            FirstUpdate = true
         end
         
         Current = Current + arg1
         if (Current > (Update + Refresh)) then
            ChatFrame1:AddMessage("OnUpdate : " .. tostring(Current))
            Update = Current
         end
      end

      ChatFrame1:AddMessage("Passage 3 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
         ChatFrame1:AddMessage("OnEvent dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 4 du file d'exécution dans le fichier lua.")


      Ce qui nous donne :

      Image

    Conclusion :
      Rapide encore une fois ; comme tout les affichages sont cette fois dans la même fenêtre, et avec un affichage contrôler, cela ne flood plus l'écran, et on comprend mieux l'ordonnance des appels fait par le jeu, et ça aide à mieux coder ses addons.

    Allez un p'tit dernier ^^, avec le calcul des FPS :

      Comme ça on prouve que le calcul des addons est bien fait entre chaque image du jeu.

      Nouveau fichier .lua :

      Image

      Code: Tout sélectionner
      local Current = 0
      local Update = 0
      local Refresh = 1
      local framePerSecond = 0

      local FirstUpdate = false

      ChatFrame1:AddMessage("Passage 1 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnLoad()
         ChatFrame1:AddMessage("OnLoad dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 2 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnUpdate(arg1)
         if FirstUpdate == false then
            ChatFrame1:AddMessage("OnUpdate premier passage.")
            FirstUpdate = true
         end
         
         framePerSecond = framePerSecond + 1
         
         Current = Current + arg1
         if (Current > (Update + Refresh)) then
            ChatFrame1:AddMessage("OnUpdate : " .. tostring(math.floor(Current)) .. ", framePerSecond : " .. tostring(framePerSecond))
            Update = Current
            framePerSecond = 0
         end
      end

      ChatFrame1:AddMessage("Passage 3 du file d'exécution dans le fichier lua.")

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
         ChatFrame1:AddMessage("OnEvent dans le fichier lua.")
      end

      ChatFrame1:AddMessage("Passage 4 du file d'exécution dans le fichier lua.")


      Ce qui nous donne :

      Image

    Conclusion :
      Là c'est très intéressant, car on voit entre l'OnUpdate premier passage, et le second passage dans OnUpdate il n'y a que 3 images d'écart.

      J'ai fais d'autres tests de mon côté en m'étant les mêmes textes mais dans d'autres addons, dont un au début de la liste des addons, quand on est sur la liste de nos personnages, et du textes dans un addon à la fin de la liste, et on voit bien l'ordre d'appel des addons qui respect l'ordre de la liste d'addon, c'est par ordre alphabétique.
Marodheure

<Hell Script>


Faire du visuel


    Enfin ^^.

    Plus haut on a vu qu'une frame c'est le conteneur de base dans l'interface de wow, elle à une position et une dimension, on va voir ça en code tout de suite. Donc retirez tout ce qui affiche du text on reviens avec un addon vide, je garde juste le code qui "mesure" le temps.

    Fichier .lua :

    Image

    Code: Tout sélectionner
    local Current = 0
    local Update = 0
    local Refresh = 1

    local frame
    local texture
    local fontString

    function MonAddon_MainFrame_OnLoad()
       frame = CreateFrame("Frame", "FrameName", UIParent)
       frame:SetPoint("TopLeft", UIParent, "TopLeft", 0, -200)
       frame:SetWidth(400)
       frame:SetHeight(400)
       
       texture = frame:CreateTexture()
       texture:SetParent(frame)
       texture:SetPoint("TopLeft", frame, "TopLeft", 0, 0)
       texture:SetWidth(frame:GetWidth())
       texture:SetHeight(frame:GetHeight())
       texture:SetTexture(1, 1, 1, 1)
       
       fontString = frame:CreateFontString()
       fontString:SetParent(frame)
       fontString:SetFont("Fonts\\FRIZQT__.TTF", 20)
       fontString:SetText("Hello World !")
       fontString:SetPoint("Center", frame, "Center", 0, 0)
       fontString:SetTextColor(0, 0, 0, 1)
    end

    function MonAddon_MainFrame_OnUpdate(arg1)
       Current = Current + arg1
       if (Current > (Update + Refresh)) then
          Update = Current
       end
    end

    function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
    end


    Voila le résultat :

    Image

    Explication :

      Donc la frame contient la texture, et la fontString. Une texture doit juste afficher une couleur ou une image, une image du jeu genre l'icone d'un sort, ou une image à vous stocké dans le dossier de l'addon, et une fontString affiche du text.

      Quand je dit "la frame contient la texture et la fontString", c'est un peu un abus de langage car, c'est juste de l'architecture, car si vous donnez à la texture une position en x plus grande que la largeur de la frame, ça vas quand même fonctionne, à vous de savoir ce que vous voulez.

    La documentation :

      Si vous ne savez pas comment marche une fonction, démerdez vous avec la documentation, vous allez dans google et vous tapez "wow api SetPoint" pour la fonction SetPoint donc ici vous prenez le premier lien et vous lisez pendant 5 minutes ou moins ou plus et vous faites des tests pendant le temps qu'il vous faut pour comprendre le bordel.

      Je vous ai linké trois sites plus, qui vous aideront à coder des addons. Voila des page qui appartiennent à ces site, qui vous aideront :


      C'est dans ces deux liens que vous passerez énormément de temps à chercher comment faire quelques chose.

    SetTexture :

      Pour afficher une image du jeu, remplacer la ligne 20 par :
      Code: Tout sélectionner
      texture:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")


      Résultat :

      Image

      Et j'ai changé la couleur du texte à la ligne 27 :
      Code: Tout sélectionner
      fontString:SetTextColor(0, 1, 0, 1)

    ======================================================================

      Pour afficher une image à vous, remplacer la ligne 20 par ces deux lignes :
      Code: Tout sélectionner
         texture:SetTexture("Interface\\Addons\\MonAddon\\TargetIcons", true)
         texture:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)


      Votre image doit être placer dans le dossier de votre addon, vous pouvez même piquer les images qui sont dans le dossiers des autres addons.

      Renseignez vous pour comprendre la fonction SetTexCoord(), elle sert à choisir la zone de l'image qui sera affichée, par exemple pour afficher juste le crâne, et pas toute l'image.

      Résultat:

      Image

      À chaque fois que vous rajouter une image dans le dossier de votre addon, vous devez relancer le jeu, car le jeu ne le sait pas, car il ne revérifie pas si le contenu du dossier Interface à changer une fois le jeu lancer, et un ReloadUI n'y changera rien.

      Vous voyez aussi que l'image s'est adapté aux dimensions de la texture, ça l'a "tordu" horizontalement, à vous de gérer l'esthétisme.

    Le format de l'image et ses caractéristiques :

      Wow peut charger des .png et des .blp, perso j'utilise des .blp, car durant mes premiers essais j'ai plus souvent réussi à faire fonctionner mes images en jeu avec des .blp qu'avec des .png, mais les deux fonctionnent si on sait comment faire.

      Pour transformer une image en .blp il faut un logiciel, j'utilise BLP2PNG.exe, lisez le fichier Readme.txt, il est pas long, et sinon vous risquez de perdre un peu de temps ^^.

      Et le détail QUI TUE TOUT, les image doivent être de largeur et de hauteur en puissance de 2 xD, elles peuvent être de forme rectangulaire mais de largeur et hauteur en puissance de 2, sinon wow ne les chargera pas, ça à beau être écrit dans la documentation de la fonction SetTexture() ça saute vraiment pas aux yeux.

    Conclusion pour faire du visuel :

      Pour faire du visuel, vous pouvez le faire en lua comme je vous montre, mais vous pouvez aussi le faire en xml dans le fichier .xml, à vous de voir ce que vous préférez.

      Pour faire un addon il faut un très grand nombre de frame, de texture, de fontString et d'autre objet que je vais détailler plus loin. C'est à vous de savoir comment vous devez faire votre addon, où vous allez mettre telle frame et telle texture.

      Par exemple dans un addon d'interface de raid, la barre de vie des joueurs est faite avec une frame et une texture dedans, et si la vie du joueur baisse il faut réduire la largeur de la frame et de la texture pour le voir visuellement, pareil pour la barre de mana, et si le joueur qui utilise cet addon veut changer la couleur de la barre de vie des rogues car il trouve le jaune trop flashy il faut tout un bordel d'architecture et une interface d'option interne à l'addon, et là on commence à bien se faire chier avec des frame dans tout les sens, et s'il veut rajouter un dégrader sur les barres de vie des joueurs il faut mixé l'affichage avec la fonction Texture:SetGradientAlpha().
Marodheure

<Hell Script>


Les objets qui existent pour faire vos addons


    Donc on a vu les frames, les textures et les fontStrings, voila une partie du reste rapidement et surtout les liens pour se renseigner dessus :

    CheckButton :

    Slider :

    DropDownMenu :
      Documentation : http://wowwiki.wikia.com/wiki/UI_Object_UIDropDownMenu
      Résumé : c'est un p'tit menu avec une liste de choix qui s'affiche quand on clique dessus, il est assez difficile à utiliser étant donné le grand nombre de paramètre qu'il faut régler pour avoir un truc sympa, mais ça en vaut la peine.

    ColorSelect:
      Documentation : http://wowwiki.wikia.com/wiki/UIOBJECT_ColorSelect
      Résumé : Ça permet de choisir une couleur, quand on a un addon personnalisable, mais cet objet est l'objet LE MOINS DOCUMENTÉ de toute l'api wow, mais ne vous en faite pas j'ai fait le taff, je vais vous passer un bout de code.
      Bout de code :
      Code: Tout sélectionner
      function ColorSelectRV2:new(parent, x, y, wh, funcRGB, funcA)
         local wheelOffsetX, opacityOffsetX, opacityW, stringRGBAOffset, editBoxRGBAW, editBoxRGBAOffset, sliderAOffsetX
         local colorSelect = CreateFrame("ColorSelect", "ColorSelect" .. tostring(self.colorSelectCount), parent.frame)
         
         wheelOffsetX = 10
         opacityOffsetX = 10
         opacityW = 32
         stringRGBAOffset = 20
         editBoxRGBAW = 60
         editBoxRGBAOffset = 10
         sliderAOffsetX = 60
         stringAOffset = 10
         
         self.colorSelectCount = self.colorSelectCount + 1
         colorSelect:SetFrameLevel(parent.frame:GetFrameLevel() + 1)
         colorSelect:SetPoint("TOPLEFT", parent.frame, "TOPLEFT", x, y)
         colorSelect:SetWidth(wheelOffsetX + wh + opacityOffsetX + opacityW)
         colorSelect:SetHeight(wh)
         colorSelect:SetScript("OnColorSelect",   function()
                                          local r, g, b = colorSelect:GetColorRGB()
                                          funcRGB(r, g, b)
                                          colorSelect.EditBoxR:SetText(tostring(math.floor(r * 100) / 100))
                                          colorSelect.EditBoxG:SetText(tostring(math.floor(g * 100) / 100))
                                          colorSelect.EditBoxB:SetText(tostring(math.floor(b * 100) / 100))
                                          if colorSelect["stringValueR"] then
                                             colorSelect["stringValueR"].obj.fontString:SetText(math.floor(r * colorSelect["stringValueR"].roundCoeff) / colorSelect["stringValueR"].roundCoeff)
                                          end
                                          if colorSelect["stringValueG"] then
                                             colorSelect["stringValueG"].obj.fontString:SetText(math.floor(g * colorSelect["stringValueR"].roundCoeff) / colorSelect["stringValueR"].roundCoeff)
                                          end
                                          if colorSelect["stringValueB"] then
                                             colorSelect["stringValueB"].obj.fontString:SetText(math.floor(b * colorSelect["stringValueR"].roundCoeff) / colorSelect["stringValueR"].roundCoeff)
                                          end
                                       end)
         
         local colorwheel = colorSelect:CreateTexture()
         colorwheel:SetWidth(wh)
         colorwheel:SetHeight(wh)
         colorwheel:SetPoint("TOPLEFT", colorSelect, "TOPLEFT", wheelOffsetX, 0)
         colorSelect:SetColorWheelTexture(colorwheel)
         
         local colorwheelthumbtexture = colorSelect:CreateTexture()
         colorwheelthumbtexture:SetTexture("Interface\\Buttons\\UI-ColorPicker-Buttons")
         colorwheelthumbtexture:SetWidth(10)
         colorwheelthumbtexture:SetHeight(10)
         colorwheelthumbtexture:SetTexCoord(0,0.15625, 0, 0.625)
         colorSelect:SetColorWheelThumbTexture(colorwheelthumbtexture)
         
         local colorvalue = colorSelect:CreateTexture()
         colorvalue:SetWidth(opacityW)
         colorvalue:SetHeight(wh)
         colorvalue:SetPoint("LEFT", colorwheel, "RIGHT", opacityOffsetX, -3)
         colorSelect:SetColorValueTexture(colorvalue)
         
         local colorvaluethumbtexture = colorSelect:CreateTexture()
         colorvaluethumbtexture:SetTexture("Interface\\Buttons\\UI-ColorPicker-Buttons")
         colorvaluethumbtexture:SetWidth(48)
         colorvaluethumbtexture:SetHeight(14)
         colorvaluethumbtexture:SetTexCoord(0.25, 1, 0.875, 0)
         colorSelect:SetColorValueThumbTexture(colorvaluethumbtexture)
         
         function colorSelect:AddStringValue(text, x, y)
            return StringRV2:new({frame = self}, {font = ToolsRV2:FontInterface(), size = 20, flags = nil, text = text, x = x, center = false, y = y, r = 1, g = 1, b = 1, a = 1}, {r = 0, g = 0, b = 0, a = 0})
         end
         
         colorSelect.stringR = colorSelect:AddStringValue("R :", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset, -15)
         colorSelect.stringG = colorSelect:AddStringValue("G :", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset, -wh / 2 + 5)
         colorSelect.stringB = colorSelect:AddStringValue("B :", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset, -wh + 20)
         
         colorSelect.EditBoxR = EditBoxRV2:new({frame = colorSelect}, "", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset + colorSelect.stringR.fontString:GetWidth() + editBoxRGBAOffset, -1, editBoxRGBAW, 40,
                                    function(text)
                                       colorSelect:SetColorRGB(text, colorSelect.EditBoxG:GetText(), colorSelect.EditBoxB:GetText())
                                    end,
                                    function(scroll, value, whellStep)
                                       if scroll == 1 and value < 1 then
                                          value = value + whellStep
                                          if value > 1 then
                                             value = 1
                                          end
                                          colorSelect.EditBoxR:SetText(tostring(value))
                                          colorSelect:SetColorRGB(tostring(value), colorSelect.EditBoxG:GetText(), colorSelect.EditBoxB:GetText())
                                       elseif scroll == -1 and value > 0 then
                                          value = value - whellStep
                                          if value < 0 then
                                             value = 0
                                          end
                                          colorSelect.EditBoxR:SetText(tostring(value))
                                          colorSelect:SetColorRGB(tostring(value), colorSelect.EditBoxG:GetText(), colorSelect.EditBoxB:GetText())
                                       end
                                    end, 0.08)
         colorSelect.EditBoxG = EditBoxRV2:new({frame = colorSelect}, "", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset + colorSelect.stringG.fontString:GetWidth() + editBoxRGBAOffset, -wh / 2 + 20, editBoxRGBAW, 40,
                                    function(text)
                                       colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), text, colorSelect.EditBoxB:GetText())
                                    end,
                                    function(scroll, value, whellStep)
                                       if scroll == 1 and value < 1 then
                                          value = value + whellStep
                                          if value > 1 then
                                             value = 1
                                          end
                                          colorSelect.EditBoxG:SetText(tostring(value))
                                          colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), tostring(value), colorSelect.EditBoxB:GetText())
                                       elseif scroll == -1 and value > 0 then
                                          value = value - whellStep
                                          if value < 0 then
                                             value = 0
                                          end
                                          colorSelect.EditBoxG:SetText(tostring(value))
                                          colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), tostring(value), colorSelect.EditBoxB:GetText())
                                       end
                                    end, 0.08)
         colorSelect.EditBoxB = EditBoxRV2:new({frame = colorSelect}, "", wheelOffsetX + wh + opacityOffsetX + opacityW + stringRGBAOffset + colorSelect.stringB.fontString:GetWidth() + editBoxRGBAOffset, -wh + 35, editBoxRGBAW, 40,
                                    function(text)
                                       colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), colorSelect.EditBoxG:GetText(), text)
                                    end,
                                    function(scroll, value, whellStep)
                                       if scroll == 1 and value < 1 then
                                          value = value + whellStep
                                          if value > 1 then
                                             value = 1
                                          end
                                          colorSelect.EditBoxB:SetText(tostring(value))
                                          colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), colorSelect.EditBoxG:GetText(), tostring(value))
                                       elseif scroll == -1 and value > 0 then
                                          value = value - whellStep
                                          if value < 0 then
                                             value = 0
                                          end
                                          colorSelect.EditBoxB:SetText(tostring(value))
                                          colorSelect:SetColorRGB(colorSelect.EditBoxR:GetText(), colorSelect.EditBoxG:GetText(), tostring(value))
                                       end
                                    end, 0.08)
         
         colorSelect.alphaSlider = SliderRV2:new({frame = colorSelect}, wheelOffsetX + wh +
                                                      opacityOffsetX + opacityW +
                                                      stringRGBAOffset + colorSelect.stringR.fontString:GetWidth() +
                                                      editBoxRGBAOffset + editBoxRGBAW +
                                                      sliderAOffsetX,  -wh / 2 + 20, "HORIZONTAL", 0, 1, 0.05, 1,
                                       function(a)
                                          funcA(a)
                                          colorSelect.EditBoxA:SetText(tostring(math.floor(a * 100) / 100))
                                       end)
         colorSelect.stringA = colorSelect:AddStringValue("A :", wheelOffsetX + wh +
                                                   opacityOffsetX + opacityW +
                                                   stringRGBAOffset + colorSelect.stringR.fontString:GetWidth() +
                                                   editBoxRGBAOffset + editBoxRGBAW +
                                                   sliderAOffsetX + stringAOffset, ({colorSelect.alphaSlider:GetPoint(1)})[5] - 40)
         colorSelect.EditBoxA = EditBoxRV2:new({frame = colorSelect}, "", wheelOffsetX + wh +
                                    opacityOffsetX + opacityW +
                                    stringRGBAOffset + colorSelect.stringR.fontString:GetWidth() +
                                    editBoxRGBAOffset + editBoxRGBAW +
                                    sliderAOffsetX + stringAOffset + colorSelect.stringA.frame:GetWidth() + 5, ({colorSelect.alphaSlider:GetPoint(1)})[5] - 25, editBoxRGBAW, 40,
                                    function(text)
                                       colorSelect.alphaSlider:SetValue(tonumber(text))
                                    end,
                                    function(scroll, value, whellStep)
                                       if scroll == 1 and value < 1 then
                                          value = value + whellStep
                                          if value > 1 then
                                             value = 1
                                          end
                                          colorSelect.EditBoxA:SetText(tostring(value))
                                          colorSelect.alphaSlider:SetValue(tostring(value))
                                       elseif scroll == -1 and value > 0 then
                                          value = value - whellStep
                                          if value < 0 then
                                             value = 0
                                          end
                                          colorSelect.EditBoxA:SetText(tostring(value))
                                          colorSelect.alphaSlider:SetValue(tostring(value))
                                       end
                                    end, 0.08)
         
         function colorSelect:SetColorRGBA(table)
            colorSelect.EditBoxR:SetText(table["R"])
            colorSelect.EditBoxG:SetText(table["G"])
            colorSelect.EditBoxB:SetText(table["B"])
            colorSelect.EditBoxA:SetText(table["A"])
            self:SetColorRGB(table["R"], table["G"], table["B"])
            self.alphaSlider:SetValue(table["A"])
         end
         
         function colorSelect:AddAlphaStringValue(alphaTable)
            self.alphaSlider:AddStringValue("RIGHT", 20, nil, alphaTable, 100)
         end
         
         function colorSelect:GetColorRGBA()
            return colorSelect.EditBoxR:GetText(), colorSelect.EditBoxG:GetText(), colorSelect.EditBoxB:GetText(), colorSelect.EditBoxA:GetText()
         end
         
         return colorSelect
      end


      Alors, je ne peux clairement pas expliqué tout ce code, c'est trop long, et en plus ça utilise des objet à moi (mais je vous met aussi le code de l'objet en question (EditBox) plus bas, car c'est un objet mal documenté aussi comme par hasard) que j'ai fais pour en simplifier la création d'interface. Mais clairement avec ce code, vous n'allez pas avoir un ColorSelect plug and play en mode je branche et ça marche xD, comme diraient les bouffons des fournisseurs internet avec leur modem. Vous avez juste à lire et regarder les fonctions et prendre ce qui semble vous intéresser pour faire votre ColorSelect.

      De plus je code en pseudo orienté objet, donc il y a des trucs que vous n'allez pas comprendre (on en reparle aussi de ça ^^).

    EditBox :
      Documentation : http://wowwiki.wikia.com/wiki/UIOBJECT_EditBox
      Résumé : C'est là où on écrit du text, la documentation est nulle sur cet objet, bonne chance xD.
      Bout de code pour vous aider :
      Code: Tout sélectionner
      function EditBoxRV2:new(parent, text, x, y, w, h, OnEnter, OnWheel, whellStep)
         local editBox
         self.editBoxCount = self.editBoxCount + 1
         editBox = CreateFrame("EditBox", "EditBox" .. tostring(self.editBoxCount), parent.frame)
         editBox:SetPoint("TOPLEFT", parent.frame, "TOPLEFT", x + 10, y)
         editBox:SetWidth(w)
         editBox:SetHeight(h)
         editBox:SetFontObject(ChatFontNormal)
         editBox:SetFont("Fonts\\FRIZQT__.TTF", 20, nil)
         editBox:SetText(text)
         editBox:SetAutoFocus(false)
         
         editBox.mySaveFct =   function()
                           local text = this:GetText()
                           if string.len(text) == 0 then
                              if whellStep then
                                 this:SetText("0")
                              else
                                 this:SetText("")
                              end
                           end
                           if OnEnter then
                              OnEnter(text)
                           end
                        end
         
         editBox:SetScript("OnShow",   function()
                                 this:ClearFocus()
                              end)
         editBox:SetScript("OnEnterPressed",   function()
                                       this:ClearFocus()
                                    end)
         editBox:SetScript("OnEscapePressed",   function()
                                          this:ClearFocus()
                                       end)
         editBox:SetScript("OnEditFocusLost",   function()
                                          this.mySaveFct()
                                       end)
         if OnWheel then
            editBox:EnableMouseWheel(true)
            editBox:SetScript("OnMouseWheel",   function()
                                          if whellStep ~= nil then
                                             OnWheel(arg1, tonumber(this:GetText()), whellStep)
                                          end
                                       end)
         end
         local left = editBox:CreateTexture(nil, "BACKGROUND1")
         left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
         left:SetTexCoord(0, 0.2, 0.15, 1)
         left:SetWidth(w * 0.2)
         left:SetHeight(h)
         left:SetPoint("TOPLEFT", editBox, "TOPLEFT", -10, -5)
         local right = editBox:CreateTexture(nil, "BACKGROUND3")
         right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
         right:SetTexCoord(0.8, 1, 0.15, 1)
         right:SetWidth(w * 0.2)
         right:SetHeight(h)
         right:SetPoint("TOPRIGHT", editBox, "TOPRIGHT", -10, -5)
         local mid = editBox:CreateTexture(nil, "BACKGROUND2")
         mid:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
         mid:SetTexCoord(0, 0.8, 0.15, 1)
         mid:SetWidth(w * 0.6)
         mid:SetHeight(h)
         mid:SetPoint("CENTER", editBox, "CENTER", -10, -5)
         
         return editBox
      end


      Même chose que pour le ColorSelect, prenez ce qui vous parait intéressant.

    Button :
      Documentation : J'ai pas de lien :/, donc je vous passe un bout de code.
      Résumé : On clique dessu et ça fait le truc que vous voulez, c'est facile à utiliser.
      Bout de code :
      Code: Tout sélectionner
      function ButtonRV2:new(parent, name, text, x, y, w, h, onClick, heritage)
         local button
         button = CreateFrame("Button", name, parent, heritage or "UIPanelButtonTemplate")
         button:SetFrameLevel(parent:GetFrameLevel() + 1)
         button:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
         button:SetWidth(w)
         button:SetHeight(h)
         if heritage == nil then
            button:SetText(text)
         end
         button:SetScript("OnClick", onClick)
         return button
      end


      Quand vous créerez votre Button faite juste ça à la ligne 3 : button = CreateFrame("Button", name, parent, "UIPanelButtonTemplate") sans écrire le "heritage or".

    FontString :
      Documentation : http://wowwiki.wikia.com/wiki/UIOBJECT_FontString
      Résumé : Petite précision pour les fontString, vous pouvez utiliser votre propre police, vous la mettez dans le dossier de votre addon, et quand vous utiliser la fonction SetFont() vous l'indiquez.

    Frame :

    Voila un exemple de ce que vous pouvez faire avec ces objets :

    Image

    Dernière chose, un addon n'est pas forcément visuel, certain addon n'ont aucune interface mais font quand même quelques chose, par exemple mon addon qui affiche le proc de la crépuscule en /crier, il vérifie les débuff de la cible et quand il trouve l'image correspondante il envois le text dans la t'chatbox.
Marodheure

<Hell Script>


Les scripts handlers attachés aux objets


    Les script handler sont des scripts qui sont exécutés lors d'une action de l'utilisateur avec sa sourie et son clavier sur votre addon (frame, texture et autre objet). Allez en bas de la page de la documentation de la frame et vous verrez la catégorie script handler. On peut voir un script handler qui s'appel OnMouseUp, c'est plutôt intéressant ^^. Il y a une catégorie script handler pour chaque objet, sauf les texture je crois, mais comme on met les textures dans des frames, ce n'est pas un soucis.

    Donc voila comment afficher un text quand on clique sur une frame :

    Image

    Code: Tout sélectionner
    local Current = 0
    local Update = 0
    local Refresh = 1

    local frame
    local texture
    local fontString

    local function OnMouseDown(self, button)
       ChatFrame1:AddMessage("un text méthode 1")
    end

    function MonAddon_MainFrame_OnLoad()
       frame = CreateFrame("Frame", "FrameName", UIParent)
       frame:SetPoint("TopLeft", UIParent, "TopLeft", 0, -200)
       frame:SetWidth(400)
       frame:SetHeight(400)
       
       texture = frame:CreateTexture()
       texture:SetParent(frame)
       texture:SetPoint("TopLeft", frame, "TopLeft", 0, 0)
       texture:SetWidth(frame:GetWidth())
       texture:SetHeight(frame:GetHeight())
       texture:SetTexture(1, 1, 1, 1)
       
       fontString = frame:CreateFontString()
       fontString:SetParent(frame)
       fontString:SetFont("Fonts\\FRIZQT__.TTF", 20)
       fontString:SetText("Hello World !")
       fontString:SetPoint("Center", frame, "Center", 0, 0)
       fontString:SetTextColor(0, 0, 0, 1)
       
    --   METHODE 1
       frame:SetScript("OnMouseDown", OnMouseDown)
       
    --   METHODE 2
       frame:SetScript("OnMouseDown",   function()
                                  ChatFrame1:AddMessage("un text méthode 2")
                               end)
       frame:EnableMouse(true)
    end

    function MonAddon_MainFrame_OnUpdate(arg1)
       Current = Current + arg1
       if (Current > (Update + Refresh)) then
          Update = Current
       end
    end

    function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
    end


    Résultat :

    Image

    Ça fonctionne de la même manière pour tout les scripts handlers, vous utilisez la fonction SetScript sur l'objet en question et basta.

    N'oubliez pas le frame:EnableMouse(true) (ligne 40) si votre frame ne sera pas utilisable par la sourie, malgrés le fait de lui affecté un script handler lié à la sourie.

    Je vous ai montré deux méthodes ligne 33 et 36, les deux fonctionnent, je vous les montre pour deux raisons, la première pour vous montrer les possibilités d'utilisation du lua, et la seconde pour vous montrez que le SetScript de la méthode 2 à écrase le SetScript de la méthode 1.

    Sauf que ça pose un problème quand vous faites un SetScript sur un objet qui n'est pas à vous, car en utilisant la fonction SetScript pour le script handler OnMouseUp vous allez peut être écraser un précédent script handler OnMouseUp qui était déjà là, et donc c'est la transition parfaite pour parler des hooks qui permet d'éviter ça.
Marodheure

<Hell Script>


Les hooks


    Il y a une documentation sur le hook, mais je l'ai jamais lu, car je ne fais que très rarement des hook sur des objet qui ne sont pas à moi, depuis que je code de addons, j'en ai fais un seul (sur le bouton pour lancer un appel dans RaidView). Donc je vais vous expliquer le hook en mode manuel, mais peut être qu'il y a une meilleure méthode dans la documentation.

    Donc par exemple sur l'interface de base, prenons le bouton d'assistance (le point d'interrogation rouge), quand on clique dessus ça affiche une fenêtre, cette fenêtre s'affiche peut être grâce à un script handler, et on vas essayer de l'écraser.

    Premièrement il faut trouver le nom de la frame qui à la texture du bouton d'assistance, on y arrive avec cette macro :
    Code: Tout sélectionner
    /run DEFAULT_CHAT_FRAME:AddMessage(tostring(GetMouseFocus():GetName()))


    Cette macro affiche le nom de la frame qui se trouve sous votre curseur. Mettez le curseur sur le bouton d'assistance lancez la macro dans la t'chatbox, et on voit que la frame s'appelle : HelpMicroButton

    Ensuite on utilise le nom de la frame dans notre addon et on lui met un script handler :

    Image

    Code: Tout sélectionner
    local Current = 0
    local Update = 0
    local Refresh = 1

    function MonAddon_MainFrame_OnLoad()
       getglobal("HelpMicroButton"):SetScript("OnMouseUp", function() ChatFrame1:AddMessage("hooked 1") end)
    end

    function MonAddon_MainFrame_OnUpdate(arg1)
       Current = Current + arg1
       if (Current > (Update + Refresh)) then
          Update = Current
       end
    end

    function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
    end


    Résultat :

    Image

    On voit que le SetScript fonctionne, mais la fenêtre d'assistance de base est toujours affichée également, cela signifie que cette fenêtre est affiché avec un autre script handler ou bien avec une autre frame superposée, donc je vais faire autrement pour vous montrer ce qu'est un hook.

    On vas créer un second SetScritp sur le même script handler (OnMouseUp), et il va falloir garder le premier sans l'écraser, donc on vas afficher deux textes différents, dans deux fonctions différentes.

    Image

    Code: Tout sélectionner
    local Current = 0
    local Update = 0
    local Refresh = 1

    function MonAddon_MainFrame_OnLoad()
    --   la méthode qui écrase le premier script OnMouseUp, si il y en a un.
       getglobal("HelpMicroButton"):SetScript("OnMouseUp", function() ChatFrame1:AddMessage("hooked 1") end)
       
       
    --   la méthode qui garde le premier script OnMouseUp, si il y en a un.
       local frame = getglobal("HelpMicroButton")
       local script = frame:GetScript("OnMouseUp")
       
       frame.ScriptHookedByMonAddon = script
       
       frame:SetScript("OnMouseUp",   function()
                                  this.ScriptHookedByMonAddon()
                                  ChatFrame1:AddMessage("hooked 2")
                               end)
    end

    function MonAddon_MainFrame_OnUpdate(arg1)
       Current = Current + arg1
       if (Current > (Update + Refresh)) then
          Update = Current
       end
    end

    function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
    end


    Résultat :

    Image

    Ça fonctionne, vous voyez ligne 12 que j'utilise la fonction GetScript, s'il n'y a pas de script la valeur retournée vaudra nil, j'ai pas mit de if qui le check car je sais que j'en ai mis un moi même à la ligne 7, mais il faut check sinon ça pourrait faire une erreur lua à la ligne 17.

    À la ligne 14 je rajoute une variable dans la variable frame, on peut faire ça car la variable frame est en fait une table, donc on peut rajouter des variable à la volée dans n'importe quelle table, et on la récupérera dans la fonction du script handler.

    À la ligne 17 vous voyez un nouveau mot clé "this", pour faire simple ça permet de choper la variable frame, car la fonction dans la quelle on se trouve est stocké dans un script handler de la variable frame, donc on a une possibilité de la récupérer.

    Conclusion :

      Les hooks c'est bien pour accrocher une de nos fonction sur une fonctionnalité déjà existant dans l'interface de base, dans un autre addon ou dans notre addon, et cela sans casser le comportement de base.
Marodheure

<Hell Script>


Les événements


    Les événements sont des informations envoyés par le jeu aux addons, et le jeu les a lui même reçut du serveur.

    Par exemple il y a un event pour dire quand un joueur rejoindre le groupe, quand un joueur gagne un buff, perd un buff, gagne un debuff, perd un debuff, quand il y a un message dans la t'chatbox, quand un ami se connect, etc.. Il y en a des centaines.

    Voila la liste de tout les event : http://wowwiki.wikia.com/wiki/Events_A-Z_(full_list)
    Et la liste par catégorie : http://wowwiki.wikia.com/wiki/Event_API

    Pour trouver votre bonheur, c'est pas simple, de plus ils sont mal documenté, si l'envie vous prend de coder un sw_stat sachez il faut gérer 20 à 30 event, on va voir comment on les gère, et pourquoi c'est compliqué dans le cas d'un sw_stat.

    L'event ADDON_LOADED :

      On va se faire un exemple concret avec l'event ADDON_LOADED.

      Pour qu'une frame reçoit un event dans sa fonction OnEvent, il faut qu'elle écoute cet event, et par défaut une frame n'en écoute aucun.

      Pour faire écouter un event à un frame on fait comme ça :

      Image

      Code: Tout sélectionner
      local Current = 0
      local Update = 0
      local Refresh = 1

      function MonAddon_MainFrame_OnLoad()
         MonAddon_MainFrame:RegisterEvent("ADDON_LOADED")
      end

      function MonAddon_MainFrame_OnUpdate(arg1)
         Current = Current + arg1
         if (Current > (Update + Refresh)) then
            Update = Current
         end
      end

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
         if event == "ADDON_LOADED" and arg1 == "MonAddon" then
         end
      end


      Regdardez la ligne 6, c'est là qu'on dit à la frame (qu'on a déclarer dans le fichier .xml) d'écoute l'event ADDON_LOADED.

      Elle l'écoute, mais à quoi il sert ? Cet event est envoyé par le jeu à chaque qu'un addon est chargé entièrement. Donc quand le jeu aura charger n'importe quel addon il enverra cet event, et en premier argument "arg1" il y aura le nom de l'addon pour l'identifier, on peut le voir dans la fonction OnEvent à la ligne 17. C'est très utile pour la collaboration inter-addon.

      Documentation de l'event ADDON_LOADED : http://wowprogramming.com/docs/events/ADDON_LOADED

      Et c'est pareil pour chaque event. Une frame peut écouter tout les event du jeu si vous le souhaitez.

    La grande question que l'on peut se poser sur les events c'est, c'est envoyé à quel moment ? Après ou avant le OnUpdate ? Ou bien de manière asynchrone ? Aucune idée ^^.
Marodheure

<Hell Script>


Les SaveVariables


    Voila la documentation des SaveVariable.

    Les SaveVariables c'est ce qui permet de sauvegarder des données dans des fichiers qui se trouvent dans le dossier WTF et des les retrouver une fois que vous relancer le jeu plus tard. Par exemple la configuration visuelle de SW_Stat est sauvegarder dans grâce aux SaveVariables.

    Pour sauvegarder les données de son addons, il y a deux méthodes, la sauvegarde commune à tout les personnages, et la sauvegarde privé au personnage avec le quel vous êtes connecté, bref j'vous explique ça un peu mieux plus bas, je vais commencer par la sauvegarde commune à tout les personnages.

    Sauvegarde commune à tout les personnages :

      Ficher .toc :


      Image

      Code: Tout sélectionner
      ## Interface: 11200
      ## Title: MonAddon
      ## SavedVariables: MonAddonDB
      MonAddon.lua
      MonAddon.xml


      Ficher .lua :


      Image

      Code: Tout sélectionner
      local Current = 0
      local Update = 0
      local Refresh = 1

      function MonAddon_MainFrame_OnLoad()
      end

      function MonAddon_MainFrame_OnUpdate(arg1)
         Current = Current + arg1
         if (Current > (Update + Refresh)) then
            Update = Current
         end
      end

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
      end


      On vient de modifier le fichier .toc, donc il faut redémarrer le jeu. Reconnectez vous, une fois en jeu, tout les addons ont accès à une variable qui s'appelle MonAddonDB, et donc le votre également, et vous allez pouvoir stocké des informations dedans, comme une variable lua. C'est la ligne 3 du fichier .toc qui a permit cela. Mais garder à l'esprit que le jeu ne sauvegardera cette variable dans un fichier qui se trouve dans le dossier WTF qu'après un ReloadUI ou une déconnexion de votre personnage, si votre jeu crash ou n'est pas fermé normalement aucune sauvegarde ne sera faites.

      Bref donc faites un ReloadUI ou une déconnexion justement. Et maintenant allez voir à l'emplacement : WoW/WTF/Account/<COMPTE>/SavedVariables/ un fichier "MonAddon.lua" vient d'être créé, faites maintenant un second ReloadUI ou une seconde déconnexion, Un fichier "MonAddon.lua.bak" vient d'être créer, ce second fichier c'est une sauvegarde de secours, on s'en fou, nous on s'occupent du premier fichier, d'ailleurs ouvrez le premier fichier "MonAddon.lua" et regardez dedans.

      Résultat :

      Image

      Voila votre variable de sauvegarde, qui pour le moment est vide. On va la remplir un peu histoire de, on peut mettre ce qu'on veut dedans.

      Fichier .lua :

      Image

      Code: Tout sélectionner
      local Current = 0
      local Update = 0
      local Refresh = 1

      function MonAddon_MainFrame_OnLoad()
         MonAddon_MainFrame:RegisterEvent("ADDON_LOADED")
      end

      function MonAddon_MainFrame_OnUpdate(arg1)
         Current = Current + arg1
         if (Current > (Update + Refresh)) then
            Update = Current
         end
      end

      function MonAddon_MainFrame_OnEvent(event, arg1, arg2, arg3, arg4)
         if event == "ADDON_LOADED" and arg1 == "MonAddon" then
            MonAddonDB =   {   2,
                           "du text",
                           2.456789,
                           ["truc"] =   {
                                       ["qwe"] = "autre chose",
                                       ["blabla"] = 555.63,
                                       [1] = "qwe",
                                       [5] = 456,
                                    }
                        }
         end
      end


      Faites un ReloadUI pour que votre addon exécute ce code, et faites un second ReloadUI pour que votre addon sauvegarde le contenu de la variable MonAddonDB dans son fichier de sauvegarde dans le dossier WTF.

      Résultat, dans le fichier WoW/WTF/Account/<COMPTE>/SavedVariables/MonAddon.lua :

      Image

      Le jeu a tout sauvegarder, et vous retrouverez ces données a chaque prochains lancement du jeu, sauf si vous décidez de vider la variable. Ne modifier jamais vous même le contenu d'un fichier dans le dossier WTF, c'est possible, mais faut pas faire d'erreur, sinon vous risquerez de perdre les données d'un addon.

    Sauvegarde privé à un personnages :

      Ficher .toc :

      Image

      Code: Tout sélectionner
      ## Interface: 11200
      ## Title: MonAddon
      ## SavedVariablesPerCharacter: OtherTestVar
      MonAddon.lua
      MonAddon.xml


      Deux choses change par rapport à la sauvegarde commune.

      1. Votre fichier sera stocké ailleurs : WoW/WTF/Account/<COMPTE>/<SERVEUR>/<PERSONNAGE>/SavedVariables/
      2. Et votre variable est unique est différente pour chaque personnage de votre compte, par exemple pour faire des profils différents. Mais pour faire des profils, personnellement je préfère tout mettre dans une grosse variable commune à tout les personnages, pour pouvoir copier le profil d'un autre personnage, car en SaveVariable privé, ce n'est pas possible, et la copie de profil est très apprécié en temps que joueur pour gagner du temps de configuration.

      Rien d'autre ne change.

    Conclusion :

      Les SaveVariables c'est génial dés que vous devez sauvegarder des données et les retrouver après une déconnexion ou une fermeture du jeu.

      Faites attention à mettre un nom unique sinon il y aura un conflit avec les autre addon.

      Il est très facile de casser un addon, en connaissant ses SaveVariables principales, genre GridDB pour grid (je dit ça au hasard), et de faire la macro /run GridDB = nil, faire un ReloadUI et toute votre configuration de grid s'est envolée.
Marodheure

<Hell Script>


Le LUA dans wow


    Le lua est un langage de programmation, voila le site officiel du langage, mais ne vous attardez pas trop dessus, car on a pas besoin de plus de 1% de la somme d'information gigantesque de ce site.

    Édit : Pour apprendre juste ce qu'il faut sur le lua allez sur ce site.

    Les limites du lua dans wow, et comment les dépasser :

      Dans wow le lua à était limité par blizzard. De base avec le lua ont peut lire et écrire dans les fichiers, se balader dans les dossiers de votre ordinateur, ouvrir des programmes, communiquer avec des serveurs extérieur etc.., mais blizzard a retiré toutes ces fonctions quand on utilise le lua dans wow pour faire des addons, et heureusement sinon on pourrait coder des virus grâce aux addons.

      Donc c'est mieux comme ça, mais ça retire un peu de liberté au développeur d'addon, par exemple pour l'addon GTS (GuildToolS), qui stock tout les items de la banque de guilde grâce aux SaveVariables expliqué au dessus. Cet addon créé une page .html qu'on peut mettre sur un serveur ftp pour voir très joliment ce que contient notre bdg, mais comment ils ont fait ? vu que blizzard à rendu impossible la communication entre les addons et "l'extérieur de wow", l'addon GTS ne créé pas la page .html directement.

      Les mecs qui ont fait GTS, on aussi coder avec le langage C, un programme (un .exe) qu'ils ont mis à disposition dans l'addon, et son but est de lire les fichier contenant les SaveVariables pour retrouver tout les items écrit et sauvegardé par l'addon. Donc on peux communiquer avec l'extérieur mais c'est chiant.

      Pareil pour les mecs qui ont fait le site RealmPlayer RealmStat, il ont un programme extérieur à wow qui envois les SaveVariables de SW_Stats sur leur serveur, et ensuite ils font un graphique avec les courbes de dps.
Marodheure

<Hell Script>


Réservé.
Marodheure

<Hell Script>


Réservé.
Marodheure

<Hell Script>


Réservé.
Marodheure

<Hell Script>


Réservé.
Marodheure

<Hell Script>


GG super topic
Apoptose

<Hell Script>


Thx ^^.
Marodheure

<Hell Script>


super topic t'es un monstre
Altamazing

<mystérieux alt>


arrogant et insupportable en bg
tu t'es arraché la! gg
Remxfly


Thx, thx, yep mais je trouverai sans doute des choses à rajouter plus tard ^^.
Marodheure

<Hell Script>


Ya de l'activité chez toi pour prendre le temps de nous pondre ça !! Avec mauvais jeux de mot bien entendu !
Sakhou

<Blacklisted>


Pas des masses, c'est les vacances ^^. Mais ça ne m'a pris en tout que 10 heures d'écriture réparties sur 1.5 jours, vu que je connais le sujet c'était rapide.
Marodheure

<Hell Script>


Ouh la ! Petite mine d'or ça ! Merci tu m'éclaircis sur des points qui me posaient problème :) Nice topic ty ^^
Löthbrohk

<ENRAGED>


Np :p
Marodheure

<Hell Script>


Merci pour ce super guide ! Il m'a vraiment aidé à débuter !

Ça serait chouette d'avoir une petite liste des boucles logiques et des arguments qui sont disponibles en lua sur WoW. Est ce qu'on peut faire une boucle while ? For ? et comment. Ce genre de chose :)
Comme tu l'as expliqué dans ta dernière partie, la masse d'information sur le lua est très importante et ce n'est pas forcement facile de s'y retrouver, et encore plus de faire le tri avec ce qui est faisable sur WoW !
Kradh

<Danse avec les Loots>


Yo, regarde ça : https://learnxinyminutes.com/docs/lua/ pour avoir une petite base sur le lua.
Marodheure

<Hell Script>


Merci !
Kradh

<Danse avec les Loots>





Conditions générales d'utilisation et mentions légales - Code de conduite des forums - Contact