[HOMMV] Empezando con Scripts

Iniciado por Abdziel, Noviembre 09, 2006, 05:22:37 PM

Abdziel

Bien, la situación es la siguiente... el editor del H5 dista mucho de ser fácilmente manejable a la hora de crear scripts.

Así que bueno, estaría bien que se aportaran algunos scripts básicos para aquellos que comiencen a "personalizar mapas" o en general dotarles de acciones y objetivos diferentes. Así pues se podrían dar casos de que una partida en multiplayer para 3 jugadores, éstos tuvieran diferentes objetivos para vencer el mapa... lo que daría a partidas completamente diferente.

Yo por mi parte he empezado a investigar en estos tipos de scripts. Y aunque por la red he llegado a ver cosas muy interesantes, no creo que sean el mejor método para empezar con los scripts (acabas con los ojos bizcos  :tongue: )

Así pues voy a explicar paso a paso la creación de un tipo de script con la función "IsObjectInRegion".

En este ejemplo lo que queremos es que cuando un determinado héroe llegué a una región en concreto, de repente aparezca delante de sus narices un ejército de criaturas neutrales.

Bueno, pues bien aquí el paso a paso:

1º: Debemos "definir" el nombre del objeto (en este caso un héroe) y el de la región que va a estar incluida en el script. Para ello debemos seleccionar el héroe que queremos utilizar y abrir el "Object Properties Tree".

Una vez allí, debemos dar el nombre que queramos utilizar para los scripts en el campo Name.

Ej: http://fotos.miarroba.com/fotos/d/f/df41b937.jpg

Ahora, haremos lo mismo para la región. Para ello seleccionamos la pestaña de "Region" y creamos el tamaño de la región.

Nota: debemos tener en cuenta las zonas de acceso del mapa permitidas para poder crear la región. Una región en una zona impasable no podrá ser "visitada". Así como habrá que estar atento a que las regiones puedan cumplir con su cometido. Así pues, si la región "requiere obligatoriamente" pasar por ella, debemos hacerlo para que al jugador no le quede más remedio que pasar por ella. Si ese no fuera el caso, entonces no nos preocuparíamos por lo último.

Importante: hay que fijares que las diferentes regiones que creemos no se "crucen" entre sí unas con otras porque nos cargaremos el juego.

Ej: http://fotos.miarroba.com/fotos/2/d/2dd43ef6.jpg

Ahora que ya tenemos el objeto y la región nombradas podemos pasar a crear el script. Podríamos haberlo hecho al revés también y no pasaría nada.

Así que ahora vamos a "Adventure Map Properties" y elegimos la pestaña que pone "Script" para abrir la consola.

Nos metemos en ella y podemos editar nuestro script.

Ej: http://fotos.miarroba.com/fotos/9/1/91caf27b.jpg

***********

El código del script es el siguiente:

if IsObjectInRegion ("Belmont", "Puente")
   then CreateMonster ("DarkKnight", 89, 10, 11, 37, 0, MONSTER_MOOD_WILD, MONSTER_COURAGE_ALWAYS_FIGHT, 180);

***********

Es importante disponer de los manuales del editor: Ids Functions y Scripts Functions, para saber como "rellenar" dichas funciones.

En nuestro caso particular podemos ver que hemos utilizado la primera función dónde definimos que Objeto y que Región son las necesarias para cumplir la condición del tipo "if".

Si la condición es verdadera entonces (then) la función CreateMonster salta. Todos los parámetros de la función vienen definidos en los manuales que se han comentado anteriormente.

En nuestro ejemplo:
- DarkKnight: es el nombre de la variable del monstruo creado por si lo queremos utilizar en otros comandos. Va entre comillas dobles como los nombres del objeto y de la región (importante).
- 89: la Id de la criatura (manual de Ids Functions).
- 10: representa el número de criaturas que queremos crear.
- 11, 37: coordenadas (x, y) dónde será creada la criatura.
- 0: indica el nivel dónde estará la criatura. "0" para la superficie. "1" para el subsuelo.
- MONSTER_MOOD_WILD: indica el caracter de la criatura creada. Podemos elegir entre varias (especificadas en el manual Script Functions) En este caso es salvaje.
- MONSTER_COURAGE_ALWAYS_FIGHT: indica la "valentía" de la criatura creada. En este caso indica que la criatura siempre luchará en cualquier circunstancia. (Nota: ahora mismo no lo recuerdo bien, pero me parece que el caracter o la valentía prevalecía una sobre la otra. Esto quiere decir que no haría falta una de las dos para que la criatura ofreciera un comportamiento totalmente hostil hacia cualquiera que intente luchar contra ella).
- 180: determina el ángulo de posicionamiento de la criatura... para que mire hacia dónde queramos. En este caso. el monstruo quedará "dado la vuelta" respecto de la posición "normal" en la que los objetos son encarados cuando arrastramos y soltamos sobre el mapa. De esta forma, la criatura creada mirará de frente a nuestro héroe.

---------------------------

Pues bien, eso ha sido todo. Siento si el post parece "Una Guía para Tontos"... seguramente aquí haya gente que sepa mucho más que yo, pero puede servir para dejar las cosas más claras a otros.

De todas maneras, a partir de aquí, por ejemplo no creo que sea necesario explicar los pasos "anteriores" al propio script en si. Ya que más o menos todos siguen un patrón similar.

Y nada... a ver si la gente se anima y conseguimos unos cuantos scripts básicos. Así, el ejemplo anterior podría complicarse mucho más, añadiendo mensajes de pantalla con algún texto descriptivo... o servir para activar un objetivo en concreto, etc...

Un saludo

P.D: por supuesto... este script... me lo ha dado como válido al "checkearlo" en la consola de scripts del editor. Pero podría no ser válido una vez dentro del juego... ya que todavía no lo he probado.  :tongue: . Así que cualquier corrección será bienvenida...  :thumbsup:

Edit1: de hecho es muy posible que el script no valga... más que nada porque seguirá activo siempre (y por lo tanto cada vez que pase por ese punto nuestro héroe pues se ejecutará de nuevo el script), ya que no tiene ningún comando que lo "pare".

Según he estado observando, parece que el método "normal" para hacer este tipo de scripts sería utilizando una función tal como sale en la "Guía Práctica" de los manuales del editor. Así que no es tan válido.  Ouch... :cry:... seguiré investigando.
[i:b6848946af]No hay final de ningún problema que no lo precedan las tinieblas; así como no hay lugar en mi alma para darte el beneplácito de la gracia. Venid pues a la servidumbre eterna...[/i:b6848946af]

Krator

Con tu base además podemos hacer más cosas, por ejemplo, si en vez de añadir una criatura en el mapa como es tu caso, queremos añadirla a un heroe, entonces cambiamos tu
[code:1]CreateMonster ("DarkKnight", 89, 10, 11, 37, 0, MONSTER_MOOD_WILD, MONSTER_COURAGE_ALWAYS_FIGHT, 180); [/code:1]
 
POR:
[code:1]AddHeroCreatures(NombreHeroe, IDCriatura, Cantidad);[/code:1]
Donde NombreHeroe es el nombre interno del heroe, en tu ejemplo sería "Belmont", también va entre comillas, por ser un literal y no un número.
IDCriatura: Mirar la tabla de ID's del editor de mapas.
Cantidad ¿Tengo que explicarlo? Es el numero de criaturas de IDCriatura que queremos añadir.

Ejemplo:
AddHeroCreatures("Belmont", 35, 10);
Añade al heroe Belmont 10 vampiros.

------------------
Cambiar la estadistica de un heroe.

[code:1]ChangeHeroStat(NombreHeroe, IDEstadistica, valor);[/code:1]
NombreHeroe: Ya lo hemos explicado anteriormente.
IDEstadistica: Puede tomar estos valores:
STAT_EXPERIENCE – La experiencia del heroe
STAT_ATTACK – Ataque
STAT_DEFENCE – Defensa
STAT_SPELL_POWER – Poder de Hechizo
STAT_KNOWLEDGE – Conocimientos
STAT_LUCK – Suerte
STAT_MORALE – Moral
STAT_MOVE_POINTS – Puntos de movimientos restantes de este turno
STAT_MANA_POINTS – Mana disponible para este momento.
Valor: El valor que queramos otorgarle.
Debemos tener en cuenta que Valor nunca puede ser negativo para la experiencia del heroe, y que el mana y los puntos de movimiento no pueden exceder del nivel máximo actual.
Ej:    ChangeHeroStat("Belmont", STAT_ATTACK, 12);
Ponemos el ataque del heroe en 12.
---------------------
Obtener artefacto.
Si lo que queremos es que se le otorgue un artefacto a un heroe, debemos de usar:
[code:1]GiveArtefact(NombreHeroe, IDArtefacto, [bindToHero = 0]);[/code:1]
Los 2 primeros ya debemos ser capaces de deducirlo, el 3º es opcional, creo que sirve para permitir o no, deshacerse del artefacto(dandoselo a otro heroe). Supongo que no hace falta escribir nada en ese lugar si no escribimos la ultima coma, pero por si acaso, ponemos 0.
Ej:    GiveArtefact("Belmont", 4, 0);
Da a Belmont el artefacto del arco de cuerno del unicornio.
--------------------
He visto ahora tu edit, bueno hombre, no te preocupes mucho. Sigue siendo muy sencillo partiendo del ejemplo del manual. Veamoslo.

[code:1]function Meeting( heroname )
 if heroname == "Christian" then
    SetPlayerResource( PLAYER_1, GOLD, 10000 );
    AddHeroCreatures( heroname, CREATURE_ARCHANGEL, 1 );
    Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", nil );
 end;
end;
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", "Meeting" );[/code:1]

Bien, el tipo ha creado un trozo de codigo que no se ejecuta porque si, si no que es ejecutado cuando se le llame, ahora lo vemos. Ese trozo corresponde a todo lo que está entre "funtion..." y el ultimo "end;". Asignandole el nombre "Meeting" (como si le llamas pericopalotes).
Lo que va entre parentesis luego, es un valor para el trozo del programa, q contiene el nombre del heroe que se meta dentro de la region.
El if ... es para saber si se llama cristian, y en caso afirmativa, hace lo que hay entre ese if, y el primer end.
Le da 1000 de oro al jugador 1, y un arcangel a aquel que esté dentro de la region(pero hemos preguntado si era cristian, y si no lo es, no se puede ejecutar este trozo, asi que tiene que ser cristian por narices).
Trigger:
Debemos mirar el manual para ver que opcion nos viene bien, pero hay tantas que me resultaria muy largo explicarlo.
En este caso REGION_ENTER_AND_STOP_TRIGGER dice el manual q sirve para cuando entras en una region determinada, y quieres que se ejecute este acto 1 sola vez.
El "metting" es el nombre de la region que le dio en este ejemplo.
nil no tenemos porque variarlo.
Acabamos ese trozo de programa con end;

Esto solo se usa si lo llamamos nosotros, eso es lo que hace el 2º trigger.
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", "Meeting" );
El primer valor es un parametro del juego que debemos buscar y conocer, el segundo es la zona de REGION_ENTER AND STOP TRIGGER, y el ultimo "Meeting", es el nombre del trozo de programa que le hayamos puesto.

EJ.
Queremos que Belmont tenga 3 de suerte al pasar por el lugar que marcó nuestro amigo en la imagen.

[code:1]function CambioMoral( heroname )
  if heroname == "Belmont" then
     ChangeHeroStat(heroname, STAT_MORALE, 3);
     Trigger( REGION_ENTER_AND_STOP_TRIGGER, "Puente", nil );
  end;
end;
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "Puente", "CambioMoral" );[/code:1]

* No he probado nada de nada del editor, y mucho menos los scripts, pero deduciendo lo he sacado, espero no errar, y sino, que me corriga alguien, please.
------------------------
Bueno, si necesitais o dudais de algun script, somos varios los programadores que pululamos por esta web, y podemos mirarnos un poquillo lo de los scripts, si os hace falta algo concreto  :thumbsup:
Doble cruzado, cuadruple placer.

Vitirr

A ver voy a aclarar unas cosillas. Abdziel, creo que tu script no funcionará. Por lo que yo sé, el fichero de scripts se ejecuta completamente en cuanto el juego comienza, así que tu función sólo comprobaría si hay un héroe en la región el día 1.

Para que parte del código se ejecute sólo cuando ocurran ciertos eventos hay que usar la función TRIGGER, que no hace otra cosa que ejecutar una función cuando ocurre un evento determinado (cuyo tipo se pasa como parámetro a TRIGGER). Hay de varios tipos, como por ejemplo el comienzo de cada día, que un héroe suba de nivel, que reclutes un héroe...

Para lo que tú quieres hay 2 eventos REGION_ENTER_AND_STOP_TRIGGER y REGION_ENTER_WITHOUT_STOP_TRIGGER. El primero se dispara cuando el héroe entra en la región y SE PARA. Es decir que si el héroe pasa porque mientras camina hacia otro punto del mapa fuera de la región el evento no se disparará. El segundo se dispara cuando el héroe entra en la región, se pare o no se pare. Al menos así es como los entiendo yo y pone en la documentación, aunque no los he probado. Dependiendo de lo que quieras hacer tendrás que usar uno u otro.

Cita de: "Krator"
tantas que me resultaria muy largo explicarlo.
En este caso REGION_ENTER_AND_STOP_TRIGGER dice el manual q sirve para cuando entras en una region determinada, y quieres que se ejecute este acto 1 sola vez.
No es así. Algo así es lo que pone en la guía práctica cuando explica el ejemplo, pero no se refiere a eso. Ese trigger se dispara como he dicho antes, lo que pasa es que en el ejemplo, en lugar de poner como parámetro una función (para que se ejecute cuando se dispare el evento) pasa el parámetro nulo "nil". Esto tiene el efecto de que el trigger deja de tener efecto, o sea, que se ejecutará la función sólo una vez.

Cita de: "Krator"Esto solo se usa si lo llamamos nosotros, eso es lo que hace el 2º trigger.
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", "Meeting" );
El primer valor es un parametro del juego que debemos buscar y conocer, el segundo es la zona de REGION_ENTER AND STOP TRIGGER, y el ultimo "Meeting", es el nombre del trozo de programa que le hayamos puesto.
El primer meeting es el nombre de la región y el segundo es el nombre de la función a ejecutar cuando se dispare el evento que se pasa como primer parámetro.

Saludos.

Lord_Ness

Saludos. Totalmente de acuerdo, he probado el script con el "cristian" incluído y variantes con ventanas de texto y otros, y funciona una sola vez; eso sí, me ha surgido una duda al ejecutar el primero: Los 10000 de oro no los añade, LOS CAMBIA. Empiezas con 30000, ejecutas el script y no tienes 40000 sino 10000 :confused:, ¿sabéis como hacer para que lo añada y no lo cambie?.
Por cierto, Abdziel, que me alegro de que las explicaciones sean así de claras porque los que estamos muy verdes en esto de programar y como dices te puedes quedar bizco intentándolo preferimos el pasito a pasito con la imagen aclaratoria, el Oh! :bounce: y esas cosas. Gracias :thumbsup:

Por otra parte los mensajes para darle historia creo que es lo más fácil de incluir en cualquier script. En el caso del "cristian" y sirve para todos sería

function Meeting( heroname )
if heroname == "Christian" then
MessageBox ( "Maps/SingleMissions/PruebaScripts2/cristian.txt");
SetPlayerResource( PLAYER_1, GOLD, 10000 );
AddHeroCreatures( heroname, CREATURE_ARCHANGEL, 1 );
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", nil );
end;
end;
Trigger( REGION_ENTER_AND_STOP_TRIGGER, "meeting", "Meeting" );

Eso sí, en el árbol de propiedades del mapa, añadir en Resources el txt nombrado.

De este modo en un mapa al menos podemos ponerle textos a cada región que editemos para que el mapa gane en historia y calidad.
Saludos.

Krator

En cuanto al dinero, no sé como funcionará en estos scripts, pero me imagino como debe ser.

Si quieres sumarle 10000 de oro, primero debes saber cuanto oro tiene el jugador, con GetPlayerResource(player, resourceKind);

El problema es que no sé como funcionan las variables en estos scripts. En programacion se usan variables como a= 1000, que quiere decir, que a vale 1000. ¿Sencillo no? Bueno, pues primero hay que decirle, que a es una variable que puede tomar un valor numerico, y es ahí donde no sé como lo hará este editor.

Genuinamente, debería ser algo como
[code:1]
OroActual = GetPlayerResource(PLAYER_1, GOLD);
OroActual = OroActual + 10000
SetPlayerResource( PLAYER_1, GOLD, OroActual );[/code:1]

Pero me encuentro con el problema de que es probable que os de error el script al decir que no sabe que es OroActual. A ver si Nam nos puede decir como funcionan las variables en los scripts del HV
Doble cruzado, cuadruple placer.

Vitirr

Efectivamente sólo hay que obtener el oro actual del jugador y establecer un nuevo valor que sea la suma del actual y la bonificación que quieres darle. No sé tampoco ahora mismo cuáles son las funciones y parámetros pero son fáciles de localizar en la documentación (en el pdf con la lista de funciones).

Otra función muy útil y que no está documentada es QuestionBox, que lo que hace es abrir una ventana con un texto y una pregunta, y dos botones, uno de sí y otro de no. Cuando el jugador pulsa en sí se ejecuta una función, cuando pulsa en no, no se hace nada. QuestionBox('/Maps/Scenario/C6M5/message-8.txt', "Info"); -- donde  el primer parámetro es el nombre de un fichero, y el segundo es el nombre de la funciónj a ejecutar si el jugador pulsa sí.

Tb se pueden agregar más parámetros, que son llamadas a funciones que se ejecutarán ANTES del question box. Por ejemplo:

QuestionBox('/Maps/Scenario/C6M5/message-8.txt', "Info", funcion1(), funcion2());

Esto hará que se ejecute la función1, después la función2 y después el question box. Esto puede ser útil por ejemplo para poner mensajes explicativos antes de un question box.

Abdziel

Cita de: "Vitirr"A ver voy a aclarar unas cosillas. Abdziel, creo que tu script no funcionará. Por lo que yo sé, el fichero de scripts se ejecuta completamente en cuanto el juego comienza, así que tu función sólo comprobaría si hay un héroe en la región el día 1.

Pues sí... mi script no era del todo correcto... lo que no sé yo si sería sólo ejecutado el día 1.

Por lo que yo sé no todas los comandos necesitan de una función específica. Así pues IsObjectInRegion, es una función con dos parámetros que devuelve un nulo si no se cumple, mientras que devuelve un distinto de nulo si se cumple. En mi caso creo que la función estaría activa en cualquier momento... que ese sería el problema real.

De todas maneras lo he cambiado... y ahora funciona con la función Trigger Id-6 (el identificador de entrar en región y parar detonante). Aunque según lo que explicas más adelante, podría ser el siguiente (Id7).

Cita de: "Vitirr"No es así. Algo así es lo que pone en la guía práctica cuando explica el ejemplo, pero no se refiere a eso. Ese trigger se dispara como he dicho antes, lo que pasa es que en el ejemplo, en lugar de poner como parámetro una función (para que se ejecute cuando se dispare el evento) pasa el parámetro nulo "nil". Esto tiene el efecto de que el trigger deja de tener efecto, o sea, que se ejecutará la función sólo una vez.

Estoy completamente de acuerdo. Ya que es eso lo que pone en el manual. La función Trigger si el último parámetro es un nulo (nil), entonces el Trigger sólo se ejecuta una vez. En el ejemplo de la guía práctica se pasa directamente un nulo... pero podría ser una variable u otra función en caso de procesos iterativos hasta que otro detonante diera como resultado un nulo y entonces el Trigger sería completamente anulado y por lo tanto la función en la que está incluido.

Para la función de sumar dinero o cualquier recurso se usa una función, por ejemplo:

function DarOro(cantidad),

En este caso Cantidad es la variable que estamos definiendo para realizar lo que es el cuerpo de la función:

[code:1]function DarOro(cantidad)
cantidad = GetPlayerResource(PLAYER_1, GOLD);
cantidad += 10000; // no sé si el Lua aceptará esta forma sintáctica.
SetPlayerResource( PLAYER_1, GOLD, OroActual );
end;[/code:1]

También podría ser válido sustituir la 3ª y 4ª línea de la función por lo siguiente:

SetPlayerResource( PLAYER_1, GOLD, cantidad+10000 );

De esta forma nos ahorraríamos un paso ya. Como el Lua se basa bastante en C, podría ser totalmente válido. Aunque lo estoy diciendo de memoría y sin haberlo testeado. Pero en principio sería la función.

------------------------

El caso es que la función set"loquesea" lo deja claro, ya que "pone a" el valor que indiquemos un parámetro.

Otra cosa sería si hubiera una función AddPlayerResource por ejemplo, en ese caso simplemente habría que ponerla y la función misma añadiría el valor que pongamos al recurso que queremos.

-------------------------

El caso es que es curioso, porque a poco que se va aportando con ejemplos así, las posibilidades se disparan tal como las propuestas hechas.

Yo he creado otro script de cuando entras en una Region... esta vez con Trigger, es la siguiente:

[code:1]function leroix (hero)
   if hero=="Belmont" then
      if GetHeroCreatures ("Belmont", 2)
         then AddHeroCreatures ("Belmont", 2, 50);
      if GetHeroCreatures ("Belmont", 3)
         then AddHeroCreatures ("Belmont", 3, 20);
      if GetHeroCreatures ("Belmont", 5)
         then AddHeroCreatures ("Belmont", 5, 12);
      if GetHeroCreatures ("Belmont", 11)
         then AddHeroCreatures ("Belmont", 11, 5);
      RemoveObjectCreatures ("fuerte", 2, 50);
      RemoveObjectCreatures ("fuerte", 3, 20);
      RemoveObjectCreatures ("fuerte", 5, 12);
      RemoveObjectCreatures ("fuerte", 11, 5);
      RemoveObject ("Sir");
      Trigger (7, "Leroix", nil);
   end;
end;

Trigger (7, "Leroix", "leroix");[/code:1]

Esta función se encarga de comprobar si el objeto está o no en la región. Y a partir de ahí se trata de añadir criaturas al ejército.

Es importante analizar la siguiente estructura:

[code:1]if GetHeroCreatures ("Belmont", 2)
         then AddHeroCreatures ("Belmont", 2, 50);[/code:1]

Lo que hace es comprobar que Belmont tiene Reclutas... en caso afirmativo lo que hace es añadir 50 reclutas más. Y así en los siguientes casos.

Hay que comentar que está preparado para no añadir criaturas que no pueden "caber" en el ejército si fuera debido a que las 7 casillas de criaturas están ocupadas por otras. Sin embargo tiene el incoveniente de que si el héroe tiene casillas libres pero no incluye reclutas... el script no los va a añadir.

Por último lo que hace es eliminar criaturas de una guarnición (fuerte es el nombre de la guarnición) y además quita una criatura del mapa de aventura (Sir).

El resultado sería que el personaje podría acabar con 50 reclutas, 20 arqueros, 12 escuderos y 5 caballeros más... sólo si los tiene incluidos en su ejército. Ese es el fallo de este script... si sólo llevaras campesinos por ejemplo... no recibirías ni una sóla criatura.

Sin embargo podría ser útil en otros scripts en los que sólo puedas añadir criaturas con prerrequisito. En este caso es necesario tener en el ejército otra unidad del mismo tipo. Pero podría resultar que debieras tener una mina de mineral o de oro para recibir ciertos refuerzos.

-------------------

Bueno pues de momento eso es todo... seguiré investigando. Y creo que podemos conseguir al menos sentar unas bases para crear mapas con scripts interesantes.

Un saludo
[i:b6848946af]No hay final de ningún problema que no lo precedan las tinieblas; así como no hay lugar en mi alma para darte el beneplácito de la gracia. Venid pues a la servidumbre eterna...[/i:b6848946af]

Vitirr

Cita de: "Abdziel"

Pues sí... mi script no era del todo correcto... lo que no sé yo si sería sólo ejecutado el día 1.
Las ordenes que pones sin trigger se ejecutan sólo el primer día. Pon por ejemplo un print y verás que el mensaje sale en la consola sólo el primer día. Tiene sentido porque ¿cuál sería la alternatiava?, ¿que se ejecutara al principio de cada día? ¿entonces para que está el trigger NEW_DAY_TIGGER?

Sobre tu otro script, yo creo que debe existir alguna función para comprobar si el héroe tiene un slot libre en su ejército, con lo cual no tendrías el problema de necesitar que ya tenga tropas de un tipo determinado para agregarle más.