You are not logged in.

Applications: [GameMaster: OPEN] | [Volunteer Testers: OPEN]


This forum will be permanently shut down on Friday 13.07.2018
Please copy or save all important information from old forum before they will be deactivated
We have moved to new board. https://forum.runesofmagic.gameforge.com/Come join us.

1

Sunday, January 29th 2012, 1:33am

[GUIA] Crea tu propio AddOn (o convierte tu macro

Parece que con CurseClient v4 y con los AddOn Pack el tema de bajar, instalar, configurar y decidir cuales son los Addones que mejor funcionan ya está muy avanzado y simplificado... así que vamos a darle una vueta de rosca más y vamos a hacer nuestros propios AddOn o bien convertir esas macros que todos tenemos en AddOns que podemos ampliar y automatizar de mejor modo y además integrarlos en la interfaz del juego sin consumir un espacio de la barra de acción.

Para empezar deberíais leeros los 2 primeros post de estos hilos:

Además si teneis algo de ingles... podeis leeros esta guia que está en la Wiki.

Esto no va a ser una traduccion del articulo de la Wiki... sino mas bien un refrito de ese articulo, cosas de la guia de las macros, algo de corta&pega de addones que todos conocemos y una pizca del amor que yo tengo por todo lo que huela a "programar".

Empecemos por el principio, un addon es un programita que se ejecuta sobre la interfaz del juego. Es como una animacion del PowerPoint o como la "negrita" del texto en Word... solo existe si tienes abierto el programa que lo contiene.

Un addon puede interactura con el juego y con al interfaz (con los objetos de la pantalla: ventanas, botones, chat, etc) de modo reducido y controlado... pero suficientemente potente.

Para crear un addon hacen falta 3 cosas:
  1. Crear un directorio dentro de la carpeta /Interface/Addons/ con el nombre del addon que estamos creando.
  2. Crear un fichero .toc con el mismo nombre que el directorio (y que el addon)
  3. y crear un fichero .lua que contenga las instrucciones que queremos ejecutar del addon (tambien con el mismo nombre)

Si además queremos que el addon tenga un aspecto visual, que se vea en la interfaz (o bien que se integre con una ventana o componente de la pantalla) tendremos que crear un fichero .xml para definir ese interfaz. Este fichero tambien sirve para que el addon interactue con los eventos del juego (cosas como "estoy en combate" o "tengo un enemigo seleccionado" o "tengo a un compañero de grupo seleccionado" o "soy el objetivo de un enemigo.. ME ATACAN!!!")

Aquí las cosas, el esquema basico es:

Source code

1
2
3
4
5
6
7
Runes of Magic/Interface/Addons
                              |- ABQ
                              |- RoMail2
                        [B]      |- MiAddon
                                     |- MiAddon.toc
                                     |- MiAddon.lua
                                     |- MiAddon.xml [/B]


El fichero .toc contiene los elementos del Addon que quiero que se "lean" al iniciar la interfaz (cuando entramos en el juego). En nuestro caso, queremos cargar solo el LUA, por ahora no vamos a crear ventana, solo a jugar un poco con el chat.

Editad el fichero MiAddon.toc y escribid en él:

Source code

1
2
3
4
5
6
## Titulo: MiAddon
## Version: 0.9 (aun no es la v1, estamos trabajando en ello)
## Notas: Interactua con el chat
## Autor: <Aqui va tu nombre>

[B]MiAddon.lua[/B]


Las "#" sirven para poner comentarios o texto en el fichero y que no se interprete como parte del codigo del programa. Sólo "MiAddon.lua" es considerado parte del programa.

Ahora cuando iniciemos la interfaz (al arrancar el juego o bien con el comando "/reloadUI") se ejecutará el codigo que pongamos dentro de MiAddon.lua, pues pongamos algo...

Editad el fichero MiAddon.lua y escribid en él:

Source code

1
DEFAULT_CHAT_FRAME:AddMessage("MiAddon se ha ejecutado!")



Con esto hacemos lo siguiente:
  • DEFAULT_CHAT_FRAME hace referencia a la pestaña "General" del chat
  • AddMessage es el comando para escibir texto en el chat

Todos los objetos de la pantalla se nombran en MAYUSCULAS como "DEFAULT_CHAT_FRAME". Todas las funciones o comandos se nombran con Mayuscula la primera de cada palabra como en "AddMenssage" y las variables o campos donde almacenar la informacion se ponen todo en minusculas. Esto no es una regla fija, es un buen consejo para identificar claramente de qué estamos hablando cuando ejecutamos codigo o cuando leemos el codigo de otros.

Recordad del post de las macros que podeis cambiar los colores del texto en la ventana de chat poniendo los codigos Hex del color: "|cffFF0000 HOLA".

Este texto que escribimos en el chat solo lo vemos nosotros... digamos que usamos el chat para "darnos informacion" de lo que ocurre a nuestro alrededor. Por ejemplo, podemos preguntar por datos de nuestro PJ:

Source code

1
2
3
4
--mi vida
 DEFAULT_CHAT_FRAME:AddMessage("Mi vida actual es: "..UnitHealth("player"))
--mi maximo
 DEFAULT_CHAT_FRAME:AddMessage("Mi maximo de vida es : "..UnitMaxHealth("player"))


Al igual que en el fichero TOC los comentarios se ponen con "#", en los ficheros LUA se poenen con "--"

Hasta ahora solo somos capaces de escribir en el chat y solo en el inicio del juego. Veamos ahora como hacer para que podamos ejecutar el codigo LUA cuando nosotros queramos (en mitad de la partida) y de este modo poder interactuar con otros PJs o con enemigos.

Para empezar lo haremos definiendo una funcion a la que llamaremos desde una macro (¿a que parece complicado?).

Editemos nuestro fichero MiAddon.lua para meter el codigo en una funcion:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--Definimos un espacio de nombres (para no pisar nombres de funcion que puedan tener otros addones.
local MiAddon= {}

--Hacemos publico el nombre para que se vea desde la macro: _G es la parte publica del juego (Game)
_G.MiAddon = MiAddon

--Nuestra funcion
function MiAddon.MiVida()
   DEFAULT_CHAT_FRAME:AddMessage("Mi vida actual es: "..UnitHealth("player"))
   DEFAULT_CHAT_FRAME:AddMessage("Mi maximo de vida es : "..UnitMaxHealth("player"))
end

--esto se ejecuta al inicio:
DEFAULT_CHAT_FRAME:AddMessage("MiAddon cargado")


Ahora creamos una macro con el texto:

Source code

1
/redactar MiAddon.MiVida()

y lo arrastramos a una casilla de la barra de accion. Cada vez que le pulses deberías ver tus puntos de vida actuales y el maximo.

Con esto ya tenemos resuelto el problema de la limitacion de caracteres de una macro.
Con exactamente el mismo programa que la macro, metido dentro de una funcion LUA, podemos agrandar el codigo a toda la logitud que necesitemos... y ejecutarlo desde un boton de la interfaz.


CUIDADO: en el codigo LUA no podeis usar comandos "barra", los que comienzan con "/". Tienes que encontrar la funcion que corresponde con ese comando.


A cambio una de las cosas que puedes hacer es difinir tus propios comandos "barra"... y hacer que con ellos se ejecuten tus funciones.

Volvemos a nuestro fichero MiAddon.lua y vamos a crear un comando "/" para ejecutar nuestra funcion y otro para ejecutar una nueva funcion que da los datos de vida del enemigo que tenemos marcado.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
--Definimos un espacio de nombres (para no pisar nombres de funcion que puedan tener otros addones.
local MiAddon= {}

--Hacemos publico el nombre para que se vea desde la macro: _G es la parte publica del juego (Game)
_G.MiAddon = MiAddon

--Nuestras funciones
function MiAddon.MiVida()
   DEFAULT_CHAT_FRAME:AddMessage("Mi vida actual es: "..UnitHealth("player"))
   DEFAULT_CHAT_FRAME:AddMessage("Mi maximo de vida es : "..UnitMaxHealth("player"))
end

[B]function MiAddon.VidaEnemigo()
   DEFAULT_CHAT_FRAME:AddMessage("Vida del enemigo: "..UnitHealth("target"))
   DEFAULT_CHAT_FRAME:AddMessage("Su maximo de vida: "..UnitMaxHealth("target"))
end[/B]

[B]--aqui van los comandos "barra"
SLASH_LaVida1 = "/LaVida" --este es el comando
SLASH_LaVida2 = "/LV" --este es un alias para el mismo comando
SlashCmdList['LaVida'] = function(editBox, msg) --esta es la funcion que se ejecuta
  if string.lower(msg) == "yo" then 
    MiAddon.MiVida()
  elseif string.lower(msg) == "tu" then
    MiAddon.VidaEnemigo()
  else
    DEFAULT_CHAT_FRAME:AddMessage("Usa: /LaVida <yo|tu> o /LV <yo|tu>")
  end
end[/B]


--esto se ejecuta al inicio:
DEFAULT_CHAT_FRAME:AddMessage("MiAddon cargado")


Vamos con la explicacion. Para añadir un comando barra se pone "SLASH_" luego el nombre de la funcion "barra" y luego un numero secuencial para los alias (diferentes formas del mismo comando), así:
  • SLASH_ + Comando + Numero = "/comando" (este es el texto que ha que escribir en el chat o en tu macro.

Siempre que vayas usando el mismo nombre y poniendo mas numeros, puedes hacer alias del comando, que puedes usar del mismo modo... son EL MISMO COMANDO.

Se usa poniendo "/LaVida yo" si quieres ver tu vida y "/LaVida tu" si quieres ver la del PJ o enemigo que tengas selecionado. Si pones unicamente "/LaVida" te presenta un mensje indicandote que debes poner "yo" o "tu" para que funcione.


Si queres definir dos comandos, uno para cada funcion, puedes hacerlo como en el ejemplo, con un "parametro"... "yo" o "tu" o con dos variables "barra":

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
...
SLASH_[B]MiVida[/B]1 = "/[B]MiVida[/B]" --este es el comando
SLASH_[B]MiVida[/B]2 = "/MV" --este es el comando
SlashCmdList["[B]MiVida[/B]"] = function(editBox, msg) --esta es la funcion que se ejecuta
     MiAddon.MiVida()
end

SLASH_[B]TuVida[/B]1 = "/[B]TuVida[/B]" --este es el comando
SLASH_[B]TuVida[/B]1 = "/TV" --este es el comando
SlashCmdList["[B]TuVida[/B]"] = function(editBox, msg) --esta es la funcion que se ejecuta
     MiAddon.VidaEnemigo()
end
...
...


Ahora se usa poniendo "/MiVida" o "/MV" si quieres ver tu vida y "/TuVida" o"TV" si quieres ver la del PJ o enemigo que tengas selecionado.


Los Eventos son sucesos del juego o de la interfaz de los que podemos enterarnos y responder a ellos de algun modo (ejecutando algunos comandos).

Para poder "escuchar" estos eventos y decidir qué acciones realizar cuando ocurran, tenemos que crear un FRAME... sí una ventana para escuchar eventos... aunque no tenga un aspecto visual (no se vea en la pantalla).

Para definir los FRAME hay que crear un fichero XML como ya hemos comentado antes.

Esta es la estructura basica para MiAddon.xml:

Source code

1
2
3
4
5
6
7
8
9
<Ui xmlns="http://www.runewaker.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.runewaker.com/UI.xsd">
    <Frame name="MIADDON_FRAME">
        <Scripts>
            <OnEvent>
                MiAddon:OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
            </OnEvent>
        </Scripts>
    </Frame>
</Ui>


ESTO NO FUNCIONA ESTA EN REVISION: En los ficheros XML, las lineas con comentarios se rodean con <!-- comentario -->

Cada vez que el juego lance un evento, nuestra funcion OnEvent se ejecuta. Decidiendo que evetos queremos atender y asignandole una accion a cada evento podemos mejorar la interaccion de nuestro Addon con el juego.

Aqui teneis la lista de los eventos que podeis monitorizar: http://www.theromwiki.com/List_of_Events

Se me olvidaba, como tenemos un nuevo archivo en el addon, hay que añadirlo al fichero TOC... MiAddon.toc ahora queda así:

Source code

1
2
3
4
5
6
7
## Titulo: MiAddon
## Version: 0.9 (aun no es la v1, estamos trabajando en ello)
## Notas: Interactua con el chat
## Autor: <Aqui va tu nombre>

MiAddon.lua
[B]MiAddon.xml[/B]


Ahora volvermos a nuestro MiAddon.lua y vamos a implementar las acciones asociadas a alguno de los eventos. No voy a mantener todo el codigo que ya llevamos escrito... pero asumo que vosotros no lo borrais, solo id añadiendo lo que vamos viendo:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
...
--el mismo nombre que hemos puesto en el XML <Frame name="MIADDON_FRAME">
local frame = _G.MIADDON_FRAME 

--Tenemos que decir que eventos escuchamos
frame:RegisterEvent("UNIT_TARGET_CHANGED")

--esta funcion nos permite convertir el nombre del evento en el nombre de la funcion que queremos ejecutar con ese evento, simplificando mucho la programacion
function MiAddon:OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
   self[event](self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
end

--y aqui van las funciones con las acciones a ejecutar tras cada evento:
function MiAddon:UNIT_TARGET_CHANGED()
    self.VidaEnemigo()
end
...
...


Definimos una variable "frame" que representará nuestro Frame "invisible" y le asociamos el evento que queremos escuchar. Definimos la funcion que atenderá los eventos "OnEvent" y basicamente le decimos que para cada evento busque una funcion en nuestro LUA con el mismo nombre que el evento ocurrido. Ahora creamos la funcion con el nombre en concreto y esperamos a que ocurre el evento para ver si funciona...

Para nuestro ejemplo, vamos a monitorizar un evento que se lanza cuando cambiamos de objetivo, de este modo con cada cambio en el chat nos aparece la vida y max del enemigo que hemos seleccionado. Este evento tambien se lanza cuando "no tenemos objetivo" es decir cuando teniendo seleccionado a uno pulsamos ESC y pasamos a no tener a ninguno seleccionado. Como no hemos tenido esto en cuenta... fallará en esta situacion.


Curiosamente el evento UNIT_TARGET_CHANGED tiene un argumento, el segundo, que identifica "que hemos seleccionado", así que podemos usarlo para distinguir si marcamos un monstruo o a nosotros mismos o si ya no tenemos objetivo.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--y aqui van las funciones con las acciones a ejecutar tras cada evento:
function MiAddon:UNIT_TARGET_CHANGED(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
       if(arg2 == 0) then
           self.MiVida()
       elseif(arg2 < 10000) 
           --esto pasa cuando tu objetivo muere o se aleja lo suficiente
           DEFAULT_CHAT_FRAME:AddMessage("El enemigo ha muerto o esta demasiado lejos")
       else
           self.VidaEnemigo()
       end

end
...
...



Como ya hemos dicho antes, no podemos usar comandos "barra" dentro de nuestro cogido LUA, así que tenemos que buscar las equivalencias de cada uno para usarlo en nuestro codigo:
  • /lanzar --> ahora es "CastSpellByName(<nombre del hechizo>)"
  • /usar --> ahora es "UseAction(<numero>) con el numero de la barra de accion.
  • /redactar --> este ya no tiene interes... ahora estamos en modo "codigo lua" y no hace falta poner esto para llamar a una funcion.
  • /wait --> este merece un seguimento aparte, a continuacion...

NOTA 1: hasta aqui está probado, corregidas algunas cosas y funcionando.
NOTA 2: he tenido que quitar los comentarios del XML porque daban un error extraño... cuando sepa porque es, lo corrijo y os lo actualizo.


=========================================================
== Esta parte la tengo que probar, porque creo que cambió en el parche 3.0
=========================================================

Ademas de los eventos del juego, de los que hemos estado hablando hasta ahora, hay un "evento" mas que podemos monitorizar, es el "refresco de la pantalla". Cada tic del reloj interno del juego, todos los elementos de la pantalla se repintan, y ese "evento" no se dispara como el resto, sino que se captura diretamente en el XML con la funcion "OnUpdate".

En nuestro MiAddon.xml añadimos:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
<Ui xmlns="http://www.runewaker.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.runewaker.com/UI.xsd">
    <Frame name="MIADDON_FRAME">
        <Scripts>
            <OnEvent>
                MiAddon:OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
            </OnEvent>
 
            <OnUpdate>
                MiAddon:OnUpdate(elapsedTime)
            </OnUpdate>
        </Scripts>
    </Frame>
</Ui>


Entonces, lo que vamos a hacer es contar cuanto tiempo ha pasado entre dos ejecuciones del OnUpdate y con eso miraremos si ya han pasado los 2 segundos de espera para la recarga de las habilidades.

En el archivo MiAddon.lua añadimos:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local time_remaining = 2 -- segundos
function MiAddon:OnUpdate(elapsed)
    -- elapsed es el tiempo en segundos transcurrido entre dos ticks del reloj
    
    time_remaining = time_remaining - elapsed
    if time_remaining > 0 then
        -- aun no han pasado los segundos
        return
    end
    time_remaining = 2 -- reseteamos a 2 segundos
    --y ejecutamos el comando
    self.MiVida()
    self.VidaEnemigo()
end


Ahora cada 2 segundos irá pintando la vida que tenemos y la que le queda a nuestro enemigo. Esta funcion seguirá ejecutandose todo el tiempo mientras juguemos... puede ser molesto porque nos llenará el chat de lineas con nuestra vida, incluso cuando no estemos luchando.


=========================================================
PROXIMAMENTE:
  • Ventanas y botones
  • Cómo interactuar con ventanas existentes en el juego
  • Cómo usar funciones o ventanas de otros addones


(continuo editando...)

by Tschuss