GBA | Scripting desde cero y hasta el infinito (Red Alien)


Like Tree39Gracias
Respuesta
 
Herramientas Desplegado
  #1  
01/09/2017
Predeterminado GBA | Scripting desde cero y hasta el infinito (Red Alien)
Muy buenas a todos los usuarios de esta hermosa comunidad.
Como muchos sabemos, tenemos por aquí una amplia gama de tutoriales sobre scripting, entre ellos han sido dos los destacados: el de Ciro y el de xabier2012.
En mi opinión y sin perjuicio de la calidad que tengan, necesitamos y merecemos una guía más completa y detallada que no sea simplemente una lista de comandos, sino una explicación tanto práctica como teórica que nos permita conocer realmente el sistema.
Partiendo con este objetivo, empezaré este humilde tutorial en el que trataré de transmitir todos los conocimientos que tengo sobre scripting.

Por otro lado, espero que ésta sea la primera guía definitiva del scripting en la que usaremos el editor de scripts de nuestro querido @cosarara97 ; cuyas ventajas no explicaré por ahora. Sólo pruébenlo y las verán por ustedes mismos.

El formato estará dividido en dos partes: una teórica sobre la lógica y una más práctica sobre el sistema de scripting en sí en el que aplicaremos lo aprendido.
Por supuesto, me llevará un tiempo terminarlo, pero iré actualizando a medida que termine la explicación sobre cada eje. Como pretendo explayar todo lo que sé, no tengo idea del orden en que lo haré por lo que puede ser modificado sobre la marcha y no pondré un índice hasta que esté terminado.

Herramientas necesarias
  • Red Alien: Descarga directa. Recomiendo, no obstante, pasarte por su blog porque la versión que tenga aquí puede quedar desactualizada.
  • A-Map: 1.95 1.92
  • La ROM base sobre la que quieran trabajar.

Parte I: Lógica
En el scripting, al igual que en cualquier tipo de programación, nos es necesario desarrollar un pensamiento lógico. Creo, por lo tanto, que la mejor manera de empezar es por allí mismo.

¿Qué es la lógica?


Definición y relación con el scripting

Dicho por DLE RAE
  • Ciencia que expone las leyes, modos y formas de las proposiciones en relación con su verdad o falsedad.
  • Modo de pensar y de actuar sensato, de sentido común.
Podemos afirmar que la lógica es una ciencia pero no una ciencia fáctica sino una formal. Es decir, no se centra en los hechos como la biología, la economía o la química, sino en sistemas de ideas. Es decir, es completamente abstracta por sí misma pero tiene una función auxiliar al ser aplicada en todas las demás ciencias.
Su objeto de estudio son las proposiciones o, mejor dicho, los procesos por los que, a partir de ellas, llegamos al conocimiento.

En la segunda definición tenemos un sentido de uso más cotidiano, la lógica entendida como sensatez: sentido común. Notarán a medida que avancemos, que lo que explicaré no será más que una formalización que nos permita desarrollar un razonamiento capaz de asegurar la validez de las conclusiones halladas.

Retomando la idea, es posible que se hayan preguntado a esta altura: ¿qué es una proposición? Simple, una oración que puede ser caracterizada como verdadera o falsa.

Por ejemplo:
  1. Está lloviendo.
  2. Mañana es martes.
  3. El sol dista, aproximadamente, 149,6 millones de kilómetros de la Tierra.
  4. Whack a Hack! es un foro de Saint Seiya.
Todos esos son ejemplos de proposiciones. Comprobemos:
  1. Acerquémonos a una ventana: ¿es verdad que llueve? Eso significa que podemos definir si es verdadera o es falsa.
  2. En este momento puntual es falsa, mañana es jueves. Sin embargo, sería verdadera si hoy fuera lunes.
  3. En este caso, es cierto. Sin embargo, no hace falta saberlo pues o la Tierra está efectivamente a esa distancia del sol (lo que la convierte en verdadera) o no lo está (haciéndola falsa).
  4. Esta oración, a menos que se decida hacer un cambio en la temática, es falsa. Sin embargo, exactamente igual que en el caso anterior, no nos importa saberlo para definir si es o no una proposición.

¿Entonces todas las oraciones son proposiciones? No, claro que no. Veamos unos ejemplos:
  1. ¿Qué día es hoy?
  2. ¡Cierra la puerta!
  3. No sé si vendrán mañana.
En estos casos, notarán que es imposible emitir un juicio sobre su veracidad. Por eso mismo, no son proposiciones, no las estudiaremos en la lógica y tampoco nos ayudarán a llegar a conclusión alguna.

Ahora se preguntarán: ¿qué utilidad tiene la lógica en el scripting?
Nos servirá tanto para armar estructuras condicionales (compare, checkflag, if) como, en un sentido mucho más amplio, para saber cómo y cuándo usar los comandos según su función.


Lógica Proposicional - Operadores Lógicos


¿Qué es la lógica proposicional?
La última vez terminamos por definir y dar ejemplos de las proposiciones. Como bien lo indica su nombre, podemos entenderla como la rama de esta ciencia que se encarga de estudiarlas. Se preguntarán, llegado este punto, ¿por qué nos interesa a nosotros? Simple: nos permitirá analizar las relaciones entre distintas proposiciones y centrarnos más en el proceso lógico que en la veracidad o la estructura de éstas.
Por ejemplo, pensemos que queremos dejar entrar al jugador a una ruta recién cuando obtenga su primer pokémon. Entonces, vamos a plantear lo siguiente: "[PLAYER] tiene al menos un pokémon en su equipo". Sabemos que puede ser verdadera o falsa y es eso lo único que nos importa pues pretendemos que, de ser verdadera, pueda pasar y, en caso contrario no lo haga, nada más.

Operadores lógicos
Hace un momento habrán leído que la lógica proposicional nos ayuda a analizar las relaciones entre proposiciones. Con esto me refería a identificar la veracidad de esta relación en función de la veracidad de cada proposición.
Bueno, bueno, ¡ya! ¿Qué quieres decir con "relación"?
¡Buena pregunta! Apaga la luz y se escapa rápidamente tras un manto de humo...
Aquí es cuando entran en juego los operadores. Vamos a poder plantear distintas relaciones entre dos o más proposiciones.

Dicho por Ejemplo útil
Pensemos en matemáticas. Supongo que todos, o al menos la gran mayoría de los que estén leyendo esto, han aprendido a sumar y a restar; quizás incluso a multiplicar y dividir. ¿Recuerdan cómo se llama cada uno de esos procesos? ¡Eso es! ¡Operaciones!
Espera, espera, espera... ¿Me estás diciendo que vamos a "sumar" o "restar" proposiciones? ¡No! Para nada. Esas son operaciones matemáticas. La lógica tiene unas operaciones diferentes pero su función es similar. Si sirve de ejemplo, pensemos que son a las proposiciones lo que las operaciones matemáticas a los números.
¿Qué relaciones u operaciones podemos plantear?
Conjunción
  1. Explicación: Suele simbolizarse como "^" y equivale a decir "y", "también" o incluso, en ocasiones, "pero".
  2. Valor de verdad: Es verdadera únicamente cuando todas las proposiciones lo son y falsa en cualquier otra ocasión.
  3. Ejemplo: Hoy es jueves y llueve. Esta conjunción será verdadera solamente si la leen un día jueves y está lloviendo. Es decir, si ambas proposiciones que forman la conjunción lo son.

Dicho por Nota para programadores
Es probable que la conozcan con el nombre de "AND".
Disyunción inclusiva
  1. Explicación: Equivale a decir "o" y se simboliza, generalmente, con "v".
  2. Valor de verdad: Es verdadera siempre y cuando al menos una de las proposiciones lo sea. Eso quiere decir que, incluso si ambas lo son, se comprueba la disyunción inclusiva. Análogamente, es falsa si y sólo si todas las proposiciones lo son.
  3. Ejemplo: Un día tiene 24 horas o 2000 minutos. Esta frase es verdadera porque un día tiene 24 horas, aún sabiendo que no tiene 2000 minutos. ¿Por qué? Simple: es una disyunción inclusiva en la que una de las proposiciones es verdadera. Por lo tanto, no importa la otra.

Dicho por Nota para programadores
¿Han oído hablar del "OR"?
Disyunción exclusiva
  1. Explicación: Lo encontramos en frases como "o bien (...), o bien (...)", "o (...) o (...) pero no ambos" y expresiones similares. Se simboliza, generalmente, con "v" o "w".
  2. Valor de verdad: Como una disyunción inclusiva, es verdadera si alguna de las dos proposiciones lo es, pero se diferencia precisamente porque si ambas son verdaderas, la disyunción exclusiva no lo es. Entonces, es falsa cuando las dos proposiciones tienen el mismo valor de verdad (sean ambas falsas o ambas verdaderas).
  3. Ejemplo: Un día tiene o bien 24 horas o bien 1440 minutos pero no ambas. Planteada como disyunción exclusiva, es falsa. ¿Por qué? Porque el día tiene 24 horas que expresadas en minutos son 1440. Entonces ambas proposiciones son verdaderas por lo que esta operación es falsa.

Dicho por Nota para programadores
Quizás la han empleado en alguna ocasión con el nombre de "XOR".
Condicional
  1. Explicación: Tal y como su nombre lo indica, es lo que necesitaremos para plantear estructuras condicionales (valga la redundancia). Lo vemos usualmente en oraciones como "si (...) entonces (...)" o "si (...), (...)". Está formado por dos partes esencialmente diferentes: antecedente y consecuente.
  2. Valor de verdad: Es lógicamente falso sólo cuando el antecedente es falso y el consecuente verdadero. En cualquier otro caso, el condicional es verdadero.
  3. Ejemplo: Si llueve, te mojas. Para que esta condición sea verdadera, debe cumplirse que siempre que llueva te mojes. Es decir, si el antecedente (llueve) es cierto, entonces el consecuente (te mojas) debe serlo sin excepción alguna.

Dicho por Nota para programadores
Supongo que han planteado estructuras condicionales en un sinfín de ocasiones con la palabra clave "IF".
Negación
  1. Explicación: Este operador niega una proposición. Suele representarse con los símbolos "-" o "~".
  2. Valor de verdad: Al negarla lo que hacemos es cambiar su valor de verdad. Si la proposición original era verdadera se vuelve falsa y viceversa.
  3. Ejemplo: Hoy no es jueves. Estamos negando la proposición "hoy es jueves". Por lo tanto, si fuera jueves la negación sería falsa y si no lo fuera, verdadera.

Dicho por Nota para programadores
Es el famosísimo operador "NOT".
Dicho por Más ejemplos
Está soleado y hace más de 30º grados. Conjunción
Está soleado pero hace menos de 30º grados. Conjunción
Si tiene sed, toma agua. Condicional
Llegarás tarde si no te apuras. Condicional
Hoy es miércoles o jueves. Disyunción exclusiva
Allí hay cemento o ladrillos. Disyunción inclusiva
No compré el sobre. Negación


Apéndice I: Tablas de verdad para operadores lógicos


Negación



Conjunción



Disyunción inclusiva



Disyunción exclusiva



Condicional




Lógica Proposicional: lenguaje simbólico


¿Cómo representamos las proposiciones?
Recordemos, chicos, que el propósito de la lógica no es evaluar la veracidad de una proposición sino la validez de un razonamiento. Es decir, debemos asegurarnos que a través de nuestro pensamiento lleguemos a conclusiones "verdaderas". Por lo tanto, más que el contenido de las proposiciones nos importará evaluar la estructura de un razonamiento.
Notaremos entonces que tener que pensarlo con cada premisa escrita completa es una pérdida de tiempo y, además, nos limita. De hacerlo así estaríamos observando solamente un caso puntual y no la estructura en sí. Por eso, representaremos cada proposición con una letra. Además, las desvincularemos de todo operador lógico. Es decir, si hubiera una negación la proposición que representaríamos sería sin el "no".
Dicho por Ejemplos
  1. Si no te despiertas temprano, perderás el turno.
    Diccionario
    p = Despertarse temprano
    q = Perder el turno
  2. Viajaré en tren o en colectivo.
    Diccionario
    p = viajar en tren
    q = viajar en colectivo
  3. No iré mañana.
    Diccionario
    p = iré mañana
¿Y qué pasa con los operadores?
Muchos habrán pensado: "¡Bien! Eso es fácil pero, ¿qué pasa con los operadores que nos dejamos en el camino? ¿No son importantes?" A ustedes les digo: ¡ESPEREN, IMPACIENTES!
Cuando expliqué cada uno de los operadores dije cómo suelen representarse. En ese momento podrán haber pensado: "¿Y eso para qué sirve?" ¡Pues para esto que hacemos ahora! ¿Ven que son impacientes?
Retomemos los ejemplos y esta vez no sólo haremos el diccionario sino que escribiremos la proposición en forma simbólica.
Dicho por Ejemplos
  1. Si no te despiertas temprano, perderás el turno.
    Diccionario
    p = Despertarse temprano
    q = Perder el turno
    ~p -> q
  2. Viajaré en tren o en colectivo.
    Diccionario
    p = viajar en tren
    q = viajar en colectivo
    p v q
  3. No iré mañana.
    Diccionario
    p = iré mañana
    ~p
  4. Compraré un auto o una camioneta pero no ambas.
    Diccionario
    p = Compraré un auto
    q = Compraré una camioneta
    p w q


Lógica Proposicional: tablas de verdad


¿Cuándo una estructura racional es lógicamente válida?
Podemos asegurar que lo es si partiendo de premisas verdaderas no llegamos nunca a una conclusión falsa. Entendemos que es una definición vaga y negativa; es decir, lo definimos por aquello que no es.
Un razonamiento inválido es aquel en el que partiendo de premisas verdaderas podemos llegar a conclusiones falsas. En oposición, uno válido es aquel en que eso no sucede NUNCA.
Ahora debemos asimilar un concepto complejo: no es cierto que, por contraposición, un razonamiento válido es aquel en que partiendo de premisas verdaderas llegamos a conclusiones verdaderas. ¿Por qué? Simple, en un razonamiento inválido bien puede ocurrir también que, siendo verdaderas las premisas, sea verdadera la conclusión.
Dicho por Ejemplo
  1. Algunos animales son mamíferos.
    Todos los perros son animales.
    Todos los perros son mamíferos.
  2. Algunas palabras son verbos.
    Todos los números son palabras.
    Todos los números son verbos.
Aquí tenemos dos razonamientos que siguen exactamente la misma estructura y en ambas las premisas son verdaderas.
En el primer caso, la conclusión es verdadera. ¿Alcanza eso para afirmar que la estructura es válida? Ya hemos dicho que no. Miremos el segundo caso: la conclusión es falsa.
Entonces podemos concluir que el razonamiento "Algunos A son B y todos los C son A; entonces todos los C son B" es inválido. Podemos dar con una conclusión cierta pero también podríamos llegar a una falsa.

Ahora la pregunta del millón... ¿Cómo haremos para saber si un razonamiento es válido o no? Usaremos un método práctico y sencillo: las tablas de verdad.
Lo que haremos será plantear las proposiciones en una tabla y combinar los valores de verdad posibles de todas las formas. Es decir, si tenemos dos proposiciones (p y q) formaremos las siguientes combinaciones de valores (donde V es verdadero y F, falso):
  1. p = V; q = F
  2. p = V; q = V
  3. p = F; q = F
  4. p = F; q = V

¡A ponerlo en práctica!
La mejor manera de entender esto, es ponerlo en práctica. Vamos a evaluar la siguiente deducción: "Miguel tiene fiebre si su temperatura corporal supera los 38ºC. Actualmente su temperatura es inferior a 38ºC, por lo tanto no tiene fiebre".
Primero que nada, vamos a pasarla al lenguaje simbólico:
Diccionario
p: Miguel tiene fiebre.
q: La temperatura corporal de Miguel supera los 38ºC.

[(q -> p) ^ ~q] -> ~p

Nota: Los operadores lógicos son binarios, es decir que unen las proposiciones de a dos. Por lo tanto, usamos los paréntesis y corchetes para estar completamente seguros de que no se malinterprete.
Recomiendo releer las explicaciones de cada operador y luego ver el apéndice donde se incluyen las tablas de verdad de cada uno para entender cómo se completa en el ejemplo siguiente.
Planteamos la tabla y completamos los posibles valores para p y q. Han de ser coherentes entre ellos, es decir, si en un renglón p es verdadera, ha de serlo todas las veces que aparezca en ese renglón.
En el paso siguiente, vamos a completar los cuadros de negación. Recordemos que ha de ser contrario al valor que tenía originalmente la proposición. Habría de quedar algo así:
Ahora sigamos el orden: vamos a completar el condicional que está dentro del paréntesis:
Luego la primer conjunción (que esá entre corchetes). Recordemos que debemos comparar el cuadro del condicional con el de la negación:
Por último, habremos de comparar la columna que acabamos de completar con la última negación. Así estaremos operando con el segundo condicional:
¡Listo! Ahora, ¿cómo sabemos si la forma de razonar es válida o no mirando la tabla? Enfoquen la vista en la columna verde (que es el resultado final de todas las operaciones). Si todos los cuadros tienen una V, se dice que el razonamiento es una tautología y significa que es válido en todos los casos, sin importar cuales sean las proposiciones.
Pero... A nosotros no nos ha quedado una tautología, ¿cierto? Porque hay un cuadro que tiene una F. ¡Muy bien! Esto se llama verdad contingente y quiere decir que el modo de razonar no es válido por sí mismo.
Un tercer caso podría ser que todas fueran "F"s. En ese caso se le llama contradicción y muestra que cualquier razonamiento que tenga esa forma, será inválido.

Veamos un segundo ejemplo: "Miguel tiene fiebre si su temperatura corporal supera los 38ºC. Actualmente no tiene fiebre, por lo tanto su temperatura corporal no supera los 38ºC".
Diccionario
p: Miguel tiene fiebre.
q: La temperatura corporal de Miguel supera los 38ºC.

[(q -> p) ^ ~p] -> ~q

Veamos la tabla:
"¡Es una tautología!" ¡Exacto! En realidad tiene una forma lógica preestablecida que se llama "Modus Tollendo Tollens" y es cierta siempre.
Básicamente nos dice que partiendo de un condicional, la negación del consecuente equivale a la negación del antecedente.
Recordatorio: Antecedente es la parte del condicional que va antes de la flecha y consecuente la que va después.

Conclusión
Un razonamiento es válido independientemente de toda variable si y sólo si todos los cuadros de la tabla (en la columna final) resultan verdaderos.


Parte II: Scripting
A medida que avancemos con los conceptos de lógica, también desarrollaremos la parte práctica: cómo escribir scripts e implementarlos en el juego.
No obstante, creo más que importante dar una repasada técnica sobre el sistema.

Teoría: Sistema de scripts


Concepto y funcionamiento del sistema

Empecemos: ¿a qué llamamos scripting? Tú que has disfrutado por horas de los juegos de pokémon habrás salteado millones de textos. Cada uno de ellos forma parte de un script.
En un principio, podemos decir que gran parte de los eventos del juego se desarrollan mediante los scripts.
Cuando hablas con tu madre antes de irte; cada vez que vuelves y tus pokémons se curan con un descanso; cuando recibes tu primer pokémon; cuando entras a la casa del Profesor Abedul y su esposa te detiene en la puerta; cuando intentas salir de Pueblo Paleta y el Profesor Oak te detiene y te lleva a su laboratorio; el hombre que da consejos en la entrada de los gimnasios... Todo eso y mucho más son ejemplos prácticos de scripts a lo largo del juego.

Intentemos explicar cómo funciona:
El juego está programado para leer un valor que se le indique y tomarlo como un índice en base al cual llama a un algoritmo" predeterminado.
Cada uno de estos "subprogramas" se asemeja a lo que, en general, llamamos "función" en el ámbito de la programación. Puede o no tanto recibir parámetros que utiliza para cumplir con cometido como devolverlos.

Por supuesto, a esta altura no andamos creando ni editando estos eventos con un editor hexadecimal: han sido creadas herramientas y, con ellas, "lenguajes". Escribimos una serie de comandos (palabras) que la propia herramienta compila; es decir, los convierte a su equivalente numérico tras lo cual el script es insertado en la ROM.
Cada una de las palabras que reconoce nuestro editor de scripts se corresponde con un número en particular (el índice mencionado anteriormente).
Por ejemplo, el comando "end" que termina el script lo veremos en un editor hexadecimal como "02".


Apéndice I: Glosario


Términos empleados en el scripting

Compilar
Convertir los comandos en su equivalente numérico. Por lo general, los editores de scripts pueden, también, insertarlo en la ROM.

Descompilar
Es el camino inverso. Consiste en tomar los números y convertirlos en las palabras correspondientes.

Offset
Como la ROM es un conjunto ordenado de información (bytes) y necesitamos saber dónde se encuentra cada uno, llamamos offset a la posición de un determinado byte. No es más que un número ordinal.

Por ejemplo, si tenemos el conjunto: {1; 2; 3; 4; 5; 6} y empezamos a numerar las posiciones desde el 0 (es decir, asignamos 0 al primer elemento, 1 al segundo, 2 al tercero y así sucesivamente) podemos decir que:
  • En el offset 0 tenemos al número 1.
  • La posición del número 4 dentro del conjunto es 3.
  • El 6 ocupa el offset 5.
Notemos que posición y offset son usados como sinónimos.
En la ROM pasa lo mismo, con la particularidad de que cada espacio es exactamente un byte y la cantidad de elementos contenidos es mucho mayor.

Offset estático
Al momento de hacer un script e insertarlo en la ROM tenemos dos opciones. La primera es decirle al programa que lo inserta el lugar exacto en que lo queremos.
Ese offset recibe el nombre de éstatico.
Dicho por Ejemplo
Si quiero que mi script empiece en la posición 0x08800000, debería escribir:

#org 0x08800000
Offset dinámico
La segunda opción consiste en darle un nombre para identificarlo. El programa que lo compila buscará espacio libre y lo insertará allí; diciéndonos luego dónde está.
Debemos indicarle a partir de qué parte empezar a buscar ese espacio libre. A ésto lo llamamos offset dinámico.
Dicho por Ejemplo
#dynamic 0x08800000
#org @inicio
El programa le asignará a @inicio el primer offset con suficiente espacio libre que encuentre, desde la posición 0x08800000 en adelante.

Las etiquetas dinámicas pueden contener letras y números mas nunca espacios.

Existen dos tipos de etiquetas dinámicas, aquellas que escribimos con @ y aquellas que llevan : (o inline labels).
La principal diferencia es que en el primero, cada parte del script se compila donde haya lugar suficiente mientras que, al usar inline labels, se compila seguido de lo que venía antes.

Dicho por Ejemplos válidos
#org @inicio
#org @start
#org @empiezaquiel1erscript
#org @1111
:start
:empiezaaquiel1erscript
:1111
Dicho por Ejemplos inválidos
#org @empieza aqui el 1er script
#org @a b
#org @super script
#org @192 21


Apéndice II: Lista de comandos


#dynamic


#dynamic
Función: Indicarle al editor dónde comenzar a buscar el espacio libre suficiente para insertar el script en la ROM.
Parámetros: 1.
  • El offset en cuestión.
Dicho por Ejemplo
#dynamic 0x800000

#org


#org
Función: Cada uno de éstos es un offset predeterminado. Es el comienzo del script y, además, cada subestructura de comandos o mensajes.
Parámetros: 1.
  • Etiqueta dinámica (@etiqueta) o estática (0x800000)
Dicho por Ejemplos
#org @comienzo
#org 0x812000

lock


lock
Función: Parar el movimiento del npc al que le hablas. Solamente se debe usar en un script de ese tipo.
Parámetros: No.
Dicho por Ejemplo
lock

loadpointer


loadpointer
Función: Cargar en la WRAM el offset que le indiquemos para que pueda ser, luego, utilizado. Por ejemplo, para mostrar un mensaje debemos primero cargar su posición y luego recién mostrarlo en pantalla.
Parámetros: 1.
  • El offset a cargar. Tanto sea como etiqueta dinámica o estática.
Dicho por Ejemplos
loadpointer @texto
loadpointer 0x930000

callstd


callstd
Función: Llamar a una rutina preestablecida.
Parámetros: 1.
  • El índice de la rutina. Tanto en decimal como en hexadecimal.
Dicho por Ejemplo
callstd 2
callstd 0x6
Tipos de callstd
  • 0: Mensaje de objeto recibido.
  • 1: Mensaje de objeto encontrado (pokéballs).
  • 2: Mensaje cuando le hablas a alguien que, además, lo fuerza a mirarte.
  • 3: Carteles.
  • 4: Abre la caja de mensajes y la mantiene así hasta que apretas A.
  • 5: Muestra un mensaje en el que puedes decidir "SI" o "NO".
  • 6: Similar al 2 pero sin que el mini mire en tu dirección.
  • 10 (0xA): Llamada del PokéNav (Emerald).

release


release
Función: Liberar al npc permitiéndolo moverse nuevamente. Debe incluirse al final del script únicamente cuando hayas usado un lock al inicio.
Parámetros: No.
Dicho por Ejemplo
release

end


end
Función: Terminar el script.
Parámetros: No.
Dicho por Ejemplo
end

Línea de texto


Línea de texto
Función: No es un comando en realidad pero al ingresar texto, debemos comenzar la línea con un signo "=".
Parámetros: No es comando.
Dicho por Ejemplos
= ¡Hey! ¿Qué tal?
= ¡Hola Mundo!
= Pokémons rulz

if


if
Función: Sirve para las estructuras condicionales. Según se comprueba verdadera o falsa la condición, realizará una acción u otra.
Parámetros: 2
  • La condición: es un número entero entre 0 y 5, cada uno con un significado diferente.
  • Otro comando: Puede ser un call o un goto. Lo que hacen es saltar a otra parte del script si se verifica la condición.
Tipos de condiciones
  • 0: Significa: "El primer valor es menor al segundo".
  • 1: Significa: "Ambos valores son iguales".
  • 2: Significa: "El primer valor es mayor al segundo".
  • 3: Significa: "El primer valor es menor o igual al segundo".
  • 4: Significa: "El primer valor es mayor o igual al segundo".
  • 5: Significa: "Los valores son distintos".

Dicho por Ejemplo
if 0x5 goto @esdistinto
if 1 goto @esigual
if 0x3 call @esmenoroigual

goto


goto
Función: Salta a un offset determinado. Es decir, ejecuta lo que hay en otra parte del script. [b]NO tiene retorno[B]
Parámetros: 1.
  • El offset al que debe ir. Tanto sea en forma de etiqueta estática o dinámica.
Dicho por Ejemplo
goto @aOtraCosaMariposa
goto 0x89000


if alto nivel


if
Función: Igual que el anterior, plantear estructuras condicionales.
Parámetros: 2
  • La condición.
  • Los comandos que deben ejecutarse si se comprueba la condición.

Tipos de condiciones
  • <: Menor
  • ==: Igual
  • >: Mayor
  • <=: Menor o igual
  • >=: Mayor o igual
  • !=: Distinto

Dicho por Ejemplo
if (0x40FF != 1) {
release
end
}

if (0x4040 >= 2) {
end
}

if (0x4000 == 5) {
loadptr :mensaje
callstd 2
}


else alto nivel


else
Función: Si la condición de un if (del spoiler anterior) no se cumple (es falsa), este comando ejecuta las instrucciones que desees.
Parámetros: 1
  • Comandos a ejecutar.
Nota: Solamente puede ir luego de un if.

Dicho por Ejemplos
if (0x40FF < 5) {
addvar 0x40FF 1
}

else {
release
end
}


if (0x4000 == 5) {
release
end
}

else {
loadptr @mensaje
callstd 6
}


addvar


addvar
Función: Suma un número al valor de una variable.
Parámetros: 2
  • El número de variable a la que queremos sumar.
  • El número que queremos sumarle.

Dicho por Ejemplo
addvar 0x40FF 5

addvar 0x4000 1

addvar 0x40F4 0xFF



¡Hola mundo!


Creando, compilando e insertando nuestro primer script
Todos aquellos que aprendimos a scriptear con el tutorial de Ciro recordaremos cómo la primera lección consistía en crear un script que no hiciera nada.
Más allá de lo anecdótico, no le veo utilidad alguna y, en todo caso, una vez que aprendan los conceptos teóricos y comprendan su funcionamiento, deberían ser totalmente capaces de hacerlo sin guía alguna si es que quieren.

Este tutorial, en cambio, comenzará de un modo mucho más trillado y tradicional; al igual que la mayoría de los cursos de programación, vamos a crear nuestro primer "¡Hola Mundo!".

¡Me encanta! Emm... ¿Y qué es eso?
Consiste en mostrar en pantalla el texto "¡Hola Mundo!". En nuestro caso, lo que haremos será que un npc nos diga eso al hablarle.

Entendiendo el editor
Lo primero que haremos será abrir Red Alien. Nos encontraremos con algo así:

#include "stdlib/std.rbh": Lo que hay enmarcado en azul es un conjunto de instrucciones. Lo que hacen es decirle al programa que cargue los archivos de la carpeta "stdlib", forma abreviada de "standard library" o, en español, "librería estándar". Cada uno de estos archivos contiene una lista de constantes, es decir, de palabras que podremos usar en lugar del valor numérico.
Una vez terminado el tutorial deberían ser capaces de entenderlo lo suficiente como para saber cuándo incluir cada archivo.
Por ejemplo, incluirlo nos permite escribir "EM_BADGE1" para referirnos a la primera medalla del pokémon Emerald en lugar de su equivalente numérico.

#dynamic 0x800000: Está explicado ya en el glosario; le indica al programa dónde comenzar a buscar espacio libre para insertar el script.

#org @main: Es un ejemplo de etiqueta dinámica; desde aquí empezarían a aparecer los comandos que conforman el script ingame.

Números: Es el número de línea contando de arriba a abajo y comenzando por el 1. Nos servirá si tenemos algún error que impida compilar.

El código
Bueno, empecemos con lo nuestro.
Lo primero será cargar la ROM, simplemente pulsamos "File" y luego "Load ROM".



Ahora debemos introducir el siguiente código:
Código:
#dynamic 0x800000
#org @main
lock
loadpointer @mensajehm
callstd 2
release
end

#org @mensajehm
= ¡Hola Mundo!

Imagen


Debería quedarles algo así:
Las líneas en blanco pueden dejarlas o no, es una cuestión estética simplemente que en nada modifica al script.


¡Genial! Pero, ¿qué hemos hecho?
Llegó el momento de explicar cada uno de los comandos usados.

#dynamic 0x800000: Como dije ya dos veces, le indica desde dónde comenzar a buscar el espacio libre.

#org @main: Es nuestra etiqueta dinámica, todo lo que haya debajo de ella se insertará en una posición libre de la ROM.

lock: Habrán notado que, dentro del juego, algunos npcs suelen caminar, correr o girar en el lugar. Cuando les hablamos, no obstante, sería bueno que dejaran su movimiento para no darte la impresión de que hablas con el aire. Éste comando hace la magia; tras usarlo, el personaje al que le hablemos se quedará quieto en el lugar.

loadpointer @mensajehm: @mensajehm es el nombre que le dimos a nuestro texto; al usarlo estamos refiriéndonos al offset donde éste se insertará.
El comando loadpointer carga en la memoria WRAM el texto que le indiquemos para que pueda ser mostrado en pantalla luego. Entonces, ésta línea se encarga de cargar el mensaje: "¡Hola Mundo!".

callstd 2: es la forma abreviada de las palabras inglesas "call standard"; "llamar estándar". Lo que hace es ejecutar una función predefinida. En éste caso, es una de las que muestra el mensaje. Para más información sobre los tipos de mensaje, revisar la lista de comandos.

release: Cada vez que pongamos un lock para frenar el movimiento, tenemos que recordar usar el release al final para que vuelva a comenzar.

end: Es el comando que finaliza el script. Siempre debe estar presente.

#org @mensajehm: Es otra etiqueta dinámica; debajo de ella irá nuestro mensaje.

= ¡Hola Mundo!: Éste es el mensaje que saldrá en pantalla. Para escribirlo tenemos que comenzar la línea con un signo "=".

Compilando e insertando el script
Una vez que ya está terminado, hacemos click en "ROM" y luego en "Compile".


Si todo fue bien, nos saldrá un mensaje indicando que el script fue compilado e insertado correctamente.


Al darle a "Ok" se abrirá otro cuadro que nos dará el offset de cada uno de las etiquetas dinámicas.


Asignando el script a un npc
Habiendo tomado nota del offset que nos dio para el inicio del script (@main, en mi caso 0xC18FFC), abrimos A-Map, nos vamos al mapa que queremos y en la pestaña de eventos seleccionamos al overworld o, en su defecto, al cuadro verde con una P encima:

Veremos que en la barra de la derecha saldrá un campo que dice: "Script offset". Ahí escribimos el que nos dio RA. Si usamos A-Map 1.95 tendremos 8 dígitos en lugar de 6, simplemente dejamos los dos primeros como 0.

Spoiler



Guardamos la ROM (Archivo->Guardar) y voilà, sólo falta probarlo...



Estructuras condicionales I: YES/NO BOX


¡El jugador puede tomar decisiones!
Con todo lo que hemos avanzado en la parte teórica de la lógica, dimos los primeros pasos para plantear estructuras condicionales. La forma más simple de hacerlo es con la famosa cajita que todos los que hayan jugado pokémon conocen; esa que nos permite tomar una decisión muy importante... ¡Decir que sí o que no!
Hoy vamos a preparar un script similar al anterior pero con una diferencia: plantearemos una estructura condicional en la que podremos elegir sí o no.
Paso a enunciar el programa que queremos lograr: "Hablamos con un NPC que nos de a elegir entre si queremos que nos salude o no. En caso de respuesta positiva, dirá "¡Hola! Un gusto saludarte."; si negamos, no dirá nada.
Ahora lo escribiré en lenguaje simbólico.
Diccionario
p: Quiero que me salude.
q: Me saluda.

p -> q

Muy bien, la cosa es simple: si le decimos que sí, saluda. No hay ninguna otra condición y por tanto no aparecerán más operaciones lógicas.

Scripteando
Código:
#include "stdlib/std.rbh"

#define dijo_no 0

#dyn 0x800000
#org @inicio
lock
faceplayer
loadptr :preguntasaludo
callstd 5
compare LASTRESULT 1
if dijo_no goto :noquiero

:siquiero
loadpointer  @Saludomensaje
callstd 6

:noquiero
release
end

:preguntasaludo
= ¿Quieres que te salude?$$

#org   @Saludomensaje
= ¡Hola!
= Un gusto saludarte.$$

Script en el RA (imagen)





Vamos a ir por partes porque aquí he hecho muchas cosas nuevas que, si no lo toman con calma y paciencia, no entenderán. Alternativamente, para aquellos que necesiten ir un poco más lento dejaré debajo un spoiler con el script escrito de una forma más directa.

#include "stdlib/std.rbh"
(...)
#dyn 0x800000
#org @inicio
lock
Hasta aquí (excluyendo el define que no he escrito) no deberían tener problemas en entenderlo. Ya lo expliqué en la parte anterior del tutorial.

faceplayer
Bueno, este comando lo que hace es que el npc al que le hablas se voltee hacia tu lado porque... Bueno, cuando hablamos con alguien nos giramos para quedar de frente.

loadptr :preguntasaludo
callstd 5
(...)

loadpointer @Saludomensaje
callstd 6
Si entendieron el otro script, se habrán dado cuenta que aquí lo que hacemos es mostrar dos mensajes en pantalla (uno para preguntar y el otro es el saludo luego de haber dicho que sí).
¡Alto ahí! En el primero no dice "loadpointer" sino "loadptr", ¿por qué? Son exactamente lo mismo. De hecho, "loadptr" es un alias, otra forma de llamar al comando loadpointer. En resumen, si queremos ahorrarnos milésimas de segundos para escribirlo abreviado, podemos usar loadptr que es lo mismo.
¿Y por qué "preguntasaludo" tiene un ":" y no un "@"? Porque podemos. Ya explicaré la diferencia cuando hable de :siquiero y :noquiero. Digamos que su función es similar a la del @, indica un "offset dinámico".
¿Y por qué un callstd 5 en lugar del 2 que usamos antes? ¡Sencillo! La función predefinida en el standard 5 es, precisamente, la que abre la caja con las opciones "YES" y "NO". Para más información revisen el apéndice de comandos.
¿Y luego por qué un callstd 6? Porque ya usamos el faceplayer antes, entonces no necesitamos el standard 2 que viene con eso incluído sino el 6 (que no lo trae).
¿Y si quiero ponerle 2? Da igual, sólo están incluyendo una operación extra e innecesaria en el procesador. No pasará nada, funcionará.

#define dijo_no 0
¡Llegamos! Esta es una parte muy interesante, ya veremos más adelante que nos ayuda mucho. Básicamente nos permite darle un valor a una palabra.
En este ejemplo, le pusimos el valor "0" a la expresión "dijo_no". Entonces, cada vez que escribamos más abajo dijo_no, lo cambiará por un 0 al compilar.
Nota: El número puede estar en decimal o en hexadecimal, da igual.

compare LASTRESULT 1
if dijo_no goto :noquiero
El compare es un comando que explicaré en detalle cuando veamos las variables. Por ahora, digamos que compara dos valores.
LASTRESULT es una variable que almacena (como su nombre lo indica) el último resultado.
Entonces, comparamos el resultado del msgbox con el número 1. Recordemos que los "resultados" son siempre valores numéricos.
Como expliqué recién, ese dijo_no equivale a un 0. Eso quiere decir que es lo mismo escribir if 0x0 goto :noquiero.
"if" significa "si" y es lo que marca nuestra estructura condicional. Comprueba una condición.
En este caso lo que nos fijamos es si la variable LASTRESULT tiene un valor menor a 1. Si lo tiene, significa que el usuario respondió negativamente. En caso contrario, el valor será exactamente 1.
Ahora pasemos al goto. Del inglés "go to" significa "ir a". Al estar después de un "if", lo que hace es saltar al offset :noquiero si y sólo si la condición es veradera. En este caso, si el valor efectivamente es menor que 1.

:siquiero
(...)
Bueno, esto me sirve para explicar la principal diferencia entre las etiquetas dinámicas con : y las que tienen @. Digamos que al usar : se compila en el lugar siguiente. Es decir, siempre seguirá ejecutándose allí. En cambio, al usar el @ lo compila donde haya lugar suficiente, no necesariamente seguido de lo anterior.

Ejemplo explicativo


Entonces debemos tener cuidado porque si hiciéramos esto:

#org @inicio
lock

:mensaje
loadpointer @texto
callstd 2

(etc)
Funcionaría, porque siempre luego del lock se ejecutaría el loadpointer.

En cambio, si lo pusiéramos así:

#org @inicio
lock

#org @mensaje
loadpointer @texto
callstd 2

(etc)
Podría suceder que funcione si el compilador encuentra espacio vació junto y quiere compilarlo allí. Pero es altamente probable que no lo haga. En ese caso, quedaríamos atrapados en un bucle sin fin pues el script no terminaría nunca y el juego se congelaría.
Recuerden, si usan @ de este modo, deben agregar un goto.

Dicho por Ejemplo
#org @inicio
lock
goto @mensaje

#org @mensaje
loadpointer @texto
callstd 2

(etc)


:noquiero
release
end
Bueno, estos comandos ya están explicados. Libera al NPC y termina el script.
Notemos que noquiero también es una inline label (que lleva :). Eso quiere decir que, si le dices que te salude, luego de hacerlo continuará ejecutando esta parte.
Entonces, usamos los mismos release y end tanto si saluda como si no. Es una forma efectiva de ahorrar espacio.

:preguntasaludo
= ¿Quieres que te salude?$$
Bueno, para los textos también podemos usar los :.
En cuanto al $$ del final, es para indicarle que allí termina el texto. De no ponerlo, podría ocurrir algún problema con el texto que intentaría seguir leyendo a continuación bytes que no corresponden.

[QUOTE]
#org [/noparse]@Saludomensaje
= ¡Hola!
= Un gusto saludarte.$$
[/QUOTE]

Otra cosa que no había mostrado hasta ahora es la capacidad de poder ecribir el mensaje en varias líneas de texto.
Para eso sólo debemos comenzar cada renglón con un "=".
Pero notemos una cosa: esto no significa que haga el salto de línea ingame. Para eso debemos aclararlo con el carácter "\n".
[QUOTE=Ejemplo]
= ¡Hola!\n
= Saludo segunda línea.
[/QUOTE]

Bueno, compilamos el script y lo asignamos a un mini tal como lo expliqué en la sección anterior y... voilà, sólo falta probarlo...
[CENTER][IMG]https://i.imgur.com/Ay6WuNu.png[/IMG][IMG]https://i.imgur.com/gOfq5dt.png[/IMG][/CENTER]

[SPOILER=Script simplificado]
[CODE][noparse]
#include "stdlib/std.rbh"

#dyn 0x800000
#org @inicio
lock
loadptr :preguntasaludo
callstd 5
compare LASTRESULT 1
if 0 goto :noquiero
loadpointer @Saludomensaje 'Esto se ejecuta si dices que sí
callstd 6

:noquiero
release
end

:preguntasaludo
= ¿Quieres que te salude?$$

#org @Saludomensaje
= ¡Hola! Un gusto saludarte.$$
[/CODE]

Aquí he dejado las inline labels (etiquetas dinámicas con :) porque, aunque no lo crean, son más que útiles y un rasgo distintivo del RA. Esfuércense por comprenderlas. Si no pueden, pregunten, ¡no muerdo! mientras explico


Variables


¡Se pueden almacenar datos dinámicamente!
Una vez llegado a este punto nos preguntaremos si la única manera de plantear una condición es dando a elegir al jugador entre sí y no. Después de todo, hay veces en que lo que deba ocurrir no depende de su decisión.
Necesitamos algo que nos permita administrar el avance del juego: marcar que algunos eventos ya han sucedido y otros aún no. Aquí es donde entran en juego las variables.
¿Y qué son las variables? Estructuras que nos permiten almacenar datos.
La mejor analogía que conozco sobre ellas la hizo Cheve al afirmar que una variable es como una alcancía en la que podemos guardar o quitar monedas. Esto es correcto precisamente porque nuestras variables sólo pueden contener números y, con más exactitud, números enteros positivos y el cero. Es decir, representaremos los datos en forma numérica.
Dicho por Nota para programadores
Las variables que usaremos son enteras, ni float, ni string ni de ningún otro tipo.
Dicho por Ejemplo explicativo
Supongamos que queremos marcar el avance de la historia: iremos guardando en nuestra "alcancía" una "moneda" por cada evento que suceda. Entonces, pensemos en FR: bajamos del cuarto, hablamos con nuestra madre y ponemos una moneda en la alcancía. En el laboratorio nos dan nuestro primer pokémon, guardamos otra.
Y así sucesivamente.
¿Y cómo usamos eso? Pensemos: al querer salir del pueblo, si aún no nos han dado nuestro primer pokémon habrá una sola moneda en la alcancía y, en caso contrario, habrá dos o más.
Pensando en eso podemos hacer un script que nos impida el paso si tenemos menos de 2 monedas.
"Entiendo pero... ¿No todo en el ROM son datos? ¿Qué diferencia a las variables de cualquier otra parte de la ROM o de la RAM?
¡Cállate, listillo! Vamor por partes: las variables se diferencian de la ROM porque son parte de la RAM. Eso quiere decir que podemos editar su valor ingame (cosa que no podemos hacer con los datos de la ROM).
¿Y qué las diferencia de cualquier otra parte de la RAM? No mucho. Solamente en que el sistema de scripting tiene comandos específicos para trabajar con ellas.

Usando las variables: ejemplo práctico
Habiendo entendido las bases, es hora de ponernos... ¡Manos a la obra!
Planteemos este ejercicio: Hagamos un script en que un NPC nos salude pero sólo la primera vez que le hablamos. Teniendo en cuenta lo que expliqué recién, es muy sencillo. Sólo tenemos que contar las monedas que haya en nuestra alcancía (o dicho de otra forma, revisar el valor de nuestra variable). Si no hay monedas en la alcancía, nos dice hola; en caso contrario, nos dirá que ya nos saludó.
Tenemos dos formas de proceder:
  1. Comparamos que el valor de la variable sea 0 y, de serlo, nos saludará.
  2. Usamos como condición que el valor de la variable sea distinto a 0 y, en caso de que se cumpla, evitamos que nos salude.
Haremos el script para ambos casos, la diferencia, de hecho, es ínfima.
El código es el siguiente:

Comparar la variable a 0 y saludar
Código:
#include "stdlib/std.rbh"

#dyn 0x800000
#org @inicio
lock
faceplayer
if (0x40FF == 0) {
    loadptr    @Saludo
    callstd 6
    addvar 0x40FF 1 'Luego de esto, la variable 0x40FF pasa a tener el valor 1
}
else {
    loadptr    @Yatesalude
    callstd 6
}
release
end

#org    @Saludo
= ¡Hola!
= Este es un saludo.$$

#org    @Yatesalude
= ¿Qué me miras?\n
= Si ya te he saludado.$$
Expliquemos un poco este código antes de proseguir. Ya sabemos qué hacen el include, el dyn, el org, el lock y el faceplayer.
Lo que sigue es una estructura condicional. La crearemos a partir de la palabra clave if.
[QUOTE]
if (0x40FF == 0) {
loadptr [/noparse]@Saludo
callstd 6
addvar 0x40FF 1
release
end
}
[/QUOTE]
Encontraremos principalmente dos partes:[list=1][*][B]if (0x40FF == 0)[/B]: Lo que hay entre paréntesis es la condición que queremos evaluar.[*][B]{ comandos }[/B]: Todo lo que hay entre llaves se ejecutará si la condición anterior se cumple, es verdadera. En este caso, si la variable 0x40FF tiene un valor de 0.[/list]
[QUOTE]
else {
loadptr @Yatesalude
callstd 6
release
end
}
[/QUOTE]
Todo este bloque de código lo pondremos solamente si queremos que se ejecute algo al no cumplirse la condición. En nuestro caso, queremos que nos diga un mensaje pero si no quisiéramos eso, podríamos ahorrarnos esto y escribir solamente el if.
[B]¿Cómo funciona esta parte?[/B] Muy sencillo: todo lo que haya entre las llaves ({}) se ejecutará si la condición del if es falsa, es decir, si no se cumple. En este caso en particular, si la variable 0x40FF tiene un valor distinto a 0.
Esto es una estructura condicional básica.
[QUOTE=Nota para programadores]
Sí, es algo que seguro conocen. Lo han visto en prácticamente todo lenguaje de programación.
[/QUOTE]

[B]Evitar saludar si el valor de la variable es distinto a 0 (mayor)[/B]
[CODE][noparse]
#include "stdlib/std.rbh"

#dyn 0x800000
#org @inicio
lock
faceplayer
if (0x40FF > 0) {
loadptr @Yatesalude
callstd 6
release
end
}
else {
loadptr @Saludo
callstd 6
addvar 0x40FF 1 'Luego de esto, la variable 0x40FF pasa a tener el valor 1
release
end
}

#org @Saludo
= ¡Hola!
= Este es un saludo.$$

#org @Yatesalude
= ¿Qué me miras?\n
= Si ya te he saludado.$$
[/CODE]

Como verán, solamente cambiamos la condición, el resto es el mismo código.
¿Y por qué ha cambiado el == por >?
¡Sencillo! Antes queríamos ver si la variable 0x40FF tenía el valor IGUAL a 0, entonces usamos el comparador ==; mientras que ahora lo que hacemos es ver si su valor es MAYOR a 0, por eso usamos el símbolo >.
Entiendo... Entonces, ¿cuántos símbolos distintos hay?
Dicho por Operadores comparativos
==: Compara si el valor de una variable es IGUAL a un número.
<: Compara si el valor de una variable es MENOR a un número.
>: Compara si el valor de una variable es MAYOR a un número.
<=: Compara si el valor de una variable es MENOR O IGUAL a un número.
>=: Compara si el valor de una variable es MAYOR O IGUAL a un número.
!=: Compara si el valor de una variable es DISTINTO a un número.
Llegado este punto y habiendo aclarado a grandes rasgos cómo funcionan las condiciones, quiero mencionar un comando nuevo que aún no he explicado:
addvar 0x40FF 1
addvar
Como su nombre indica, lo que hace es sumar al valor de la variable el número que le indiquemos.
Este comando lleva dos parámetros que son los siguientes:
  1. Número de la variable.
  2. Valor a sumarle.
Dicho por Nota para programadores
La sintaxis equivalente en algún lenguaje de programación podría ser la siguiente:
  • variable++
  • variable += 1
  • variable = variable + 1
Resultado
Una vez insertado en la ROM, lo probamos y obtendremos este resultado, al hablarle la primera vez (izquierda) y al hablarle todas las demás veces (derecha):

VISCA CATALUNYA!
LLIBERTAT


"Lluitem pacíficament, perquè és l'essència de la llibertat. Dubtem com qualsevol persona, perquè és l'antídot a la temeritat. Parlem com a demòcrates, perquè és la clau de l'èxit. Confiem els uns amb els altres, perquè serà la derrota dels tirans"

Última edición por Jack Johnson; 23/08/2019 a las 13:42 Razón: Corrección de formato
  #2  
01/09/2017
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Buen aporte Francu, muy bien elaborado y super detallado!, esta clase de aportes valen la pena c:
TE AMO COMIC SANS
  #3  
01/09/2017
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
El comienzo de lo que puede ser un gran tutorial sin duda. A mi parecer todo muy bien explicado, claro y conciso.
No he probado mucho el Red Alien, pero después de esto tengo curiosidad por saber las ventajas que nos puede llegar a aportar.
Sigue así!!
La diferencia entre la genialidad y la estupidez es que la genialidad tiene un límite.
~Albert Einstein

  #4  
04/09/2017
Predeterminado Re: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
¡Buenisimo! no se te escapa uno, todo super detallado

Ya lo probé y va exelente:

Scan



Espero no quede colgado y nos traigas más.
  #5  
04/09/2017
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Excelente!! y muy bien explicado. Espero y continúes.

Estaré al pendiente de cada actualización !! Así que éxitos


·Gracias WaHeros y WaHeras·

---------------------
·Gracias Copetin·______·Gracias Lunita·



·No olviden visitar mis galerías·



......


·Proyectos que apoyo·



......


  #6  
05/09/2017
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Buen post yo quiero especializarme en scripts y me ayudo este tutorial, gracias
  #7  
05/09/2017
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Conque al fin decides postearlo ¿he?

Al fin un tuto de scripting que resalta entre el montón de tutoriales de XSE.

Hasta ahora has explicado lo básico y de un modo sencillo. Buen trabajo.
Me parece genial la portabilidad que aporta RA de cara a la inclusión de nuevos comandos y todo eso. Espléndido, magnífico tuto!
Discord: DrakoVinyl #3790
  #8  
31/01/2018
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Buenas, buenas, buenas. Tras tanto tiempo, he vuelto para actualizar esto.

Parte I: Lógica

Lógica Proposicional - Operadores Lógicos


¿Qué es la lógica proposicional?
La última vez terminamos por definir y dar ejemplos de las proposiciones. Como bien lo indica su nombre, podemos entenderla como la rama de esta ciencia que se encarga de estudiarlas. Se preguntarán, llegado este punto, ¿por qué nos interesa a nosotros? Simple: nos permitirá analizar las relaciones entre distintas proposiciones y centrarnos más en el proceso lógico que en la veracidad o la estructura de éstas.
Por ejemplo, pensemos que queremos dejar entrar al jugador a una ruta recién cuando obtenga su primer pokémon. Entonces, vamos a plantear lo siguiente: "[PLAYER] tiene al menos un pokémon en su equipo". Sabemos que puede ser verdadera o falsa y es eso lo único que nos importa pues pretendemos que, de ser verdadera, pueda pasar y, en caso contrario no lo haga, nada más.

Operadores lógicos
Hace un momento habrán leído que la lógica proposicional nos ayuda a analizar las relaciones entre proposiciones. Con esto me refería a identificar la veracidad de esta relación en función de la veracidad de cada proposición.
Bueno, bueno, ¡ya! ¿Qué quieres decir con "relación"?
¡Buena pregunta! Apaga la luz y se escapa rápidamente tras un manto de humo...
Aquí es cuando entran en juego los operadores. Vamos a poder plantear distintas relaciones entre dos o más proposiciones.

Dicho por Ejemplo útil
Pensemos en matemáticas. Supongo que todos, o al menos la gran mayoría de los que estén leyendo esto, han aprendido a sumar y a restar; quizás incluso a multiplicar y dividir. ¿Recuerdan cómo se llama cada uno de esos procesos? ¡Eso es! ¡Operaciones!
Espera, espera, espera... ¿Me estás diciendo que vamos a "sumar" o "restar" proposiciones? ¡No! Para nada. Esas son operaciones matemáticas. La lógica tiene unas operaciones diferentes pero su función es similar. Si sirve de ejemplo, pensemos que son a las proposiciones lo que las operaciones matemáticas a los números.
¿Qué relaciones u operaciones podemos plantear?
Conjunción
  1. Explicación: Suele simbolizarse como "^" y equivale a decir "y", "también" o incluso, en ocasiones, "pero".
  2. Valor de verdad: Es verdadera únicamente cuando todas las proposiciones lo son y falsa en cualquier otra ocasión.
  3. Ejemplo: Hoy es jueves y llueve. Esta conjunción será verdadera solamente si la leen un día jueves y está lloviendo. Es decir, si ambas proposiciones que forman la conjunción lo son.

Dicho por Nota para programadores
Es probable que la conozcan con el nombre de "AND".
Disyunción inclusiva
  1. Explicación: Equivale a decir "o" y se simboliza, generalmente, con "v".
  2. Valor de verdad: Es verdadera siempre y cuando al menos una de las proposiciones lo sea. Eso quiere decir que, incluso si ambas lo son, se comprueba la disyunción inclusiva. Análogamente, es falsa si y sólo si todas las proposiciones lo son.
  3. Ejemplo: Un día tiene 24 horas o 2000 minutos. Esta frase es verdadera porque un día tiene 24 horas, aún sabiendo que no tiene 2000 minutos. ¿Por qué? Simple: es una disyunción inclusiva en la que una de las proposiciones es verdadera. Por lo tanto, no importa la otra.

Dicho por Nota para programadores
¿Han oído hablar del "OR"?
Disyunción exclusiva
  1. Explicación: Lo encontramos en frases como "o bien (...), o bien (...)", "o (...) o (...) pero no ambos" y expresiones similares. Se simboliza, generalmente, con "v" o "w".
  2. Valor de verdad: Como una disyunción inclusiva, es verdadera si alguna de las dos proposiciones lo es, pero se diferencia precisamente porque si ambas son verdaderas, la disyunción exclusiva no lo es. Entonces, es falsa cuando las dos proposiciones tienen el mismo valor de verdad (sean ambas falsas o ambas verdaderas).
  3. Ejemplo: Un día tiene o bien 24 horas o bien 1440 minutos pero no ambas. Planteada como disyunción exclusiva, es falsa. ¿Por qué? Porque el día tiene 24 horas que expresadas en minutos son 1440. Entonces ambas proposiciones son verdaderas por lo que esta operación es falsa.

Dicho por Nota para programadores
Quizás la han empleado en alguna ocasión con el nombre de "XOR".
Condicional
  1. Explicación: Tal y como su nombre lo indica, es lo que necesitaremos para plantear estructuras condicionales (valga la redundancia). Lo vemos usualmente en oraciones como "si (...) entonces (...)" o "si (...), (...)". Está formado por dos partes esencialmente diferentes: antecedente y consecuente.
  2. Valor de verdad: Es lógicamente falso sólo cuando el antecedente es falso y el consecuente verdadero. En cualquier otro caso, el condicional es verdadero.
  3. Ejemplo: Si llueve, te mojas. Para que esta condición sea verdadera, debe cumplirse que siempre que llueva te mojes. Es decir, si el antecedente (llueve) es cierto, entonces el consecuente (te mojas) debe serlo sin excepción alguna.

Dicho por Nota para programadores
Supongo que han planteado estructuras condicionales en un sinfín de ocasiones con la palabra clave "IF".
Negación
  1. Explicación: Este operador niega una proposición. Suele representarse con los símbolos "-" o "~".
  2. Valor de verdad: Al negarla lo que hacemos es cambiar su valor de verdad. Si la proposición original era verdadera se vuelve falsa y viceversa.
  3. Ejemplo: Hoy no es jueves. Estamos negando la proposición "hoy es jueves". Por lo tanto, si fuera jueves la negación sería falsa y si no lo fuera, verdadera.

Dicho por Nota para programadores
Es el famosísimo operador "NOT".
Dicho por Más ejemplos
Está soleado y hace más de 30º grados. Conjunción
Está soleado pero hace menos de 30º grados. Conjunción
Si tiene sed, toma agua. Condicional
Llegarás tarde si no te apuras. Condicional
Hoy es miércoles o jueves. Disyunción exclusiva
Allí hay cemento o ladrillos. Disyunción inclusiva
No compré el sobre. Negación


Apéndice I: Tablas de verdad para operadores lógicos


Negación



Conjunción



Disyunción inclusiva



Disyunción exclusiva



Condicional




Lógica Proposicional: lenguaje simbólico


¿Cómo representamos las proposiciones?
Recordemos, chicos, que el propósito de la lógica no es evaluar la veracidad de una proposición sino la validez de un razonamiento. Es decir, debemos asegurarnos que a través de nuestro pensamiento lleguemos a conclusiones "verdaderas". Por lo tanto, más que el contenido de las proposiciones nos importará evaluar la estructura de un razonamiento.
Notaremos entonces que tener que pensarlo con cada premisa escrita completa es una pérdida de tiempo y, además, nos limita. De hacerlo así estaríamos observando solamente un caso puntual y no la estructura en sí. Por eso, representaremos cada proposición con una letra. Además, las desvincularemos de todo operador lógico. Es decir, si hubiera una negación la proposición que representaríamos sería sin el "no".
Dicho por Ejemplos
  1. Si no te despiertas temprano, perderás el turno.
    Diccionario
    p = Despertarse temprano
    q = Perder el turno
  2. Viajaré en tren o en colectivo.
    Diccionario
    p = viajar en tren
    q = viajar en colectivo
  3. No iré mañana.
    Diccionario
    p = iré mañana
¿Y qué pasa con los operadores?
Muchos habrán pensado: "¡Bien! Eso es fácil pero, ¿qué pasa con los operadores que nos dejamos en el camino? ¿No son importantes?" A ustedes les digo: ¡ESPEREN, IMPACIENTES!
Cuando expliqué cada uno de los operadores dije cómo suelen representarse. En ese momento podrán haber pensado: "¿Y eso para qué sirve?" ¡Pues para esto que hacemos ahora! ¿Ven que son impacientes?
Retomemos los ejemplos y esta vez no sólo haremos el diccionario sino que escribiremos la proposición en forma simbólica.
Dicho por Ejemplos
  1. Si no te despiertas temprano, perderás el turno.
    Diccionario
    p = Despertarse temprano
    q = Perder el turno
    ~p -> q
  2. Viajaré en tren o en colectivo.
    Diccionario
    p = viajar en tren
    q = viajar en colectivo
    p v q
  3. No iré mañana.
    Diccionario
    p = iré mañana
    ~p
  4. Compraré un auto o una camioneta pero no ambas.
    Diccionario
    p = Compraré un auto
    q = Compraré una camioneta
    p w q


Lógica Proposicional: tablas de verdad


¿Cuándo una estructura racional es lógicamente válida?
Podemos asegurar que lo es si partiendo de premisas verdaderas no llegamos nunca a una conclusión falsa. Entendemos que es una definición vaga y negativa; es decir, lo definimos por aquello que no es.
Un razonamiento inválido es aquel en el que partiendo de premisas verdaderas podemos llegar a conclusiones falsas. En oposición, uno válido es aquel en que eso no sucede NUNCA.
Ahora debemos asimilar un concepto complejo: no es cierto que, por contraposición, un razonamiento válido es aquel en que partiendo de premisas verdaderas llegamos a conclusiones verdaderas. ¿Por qué? Simple, en un razonamiento inválido bien puede ocurrir también que, siendo verdaderas las premisas, sea verdadera la conclusión.
Dicho por Ejemplo
  1. Algunos animales son mamíferos.
    Todos los perros son animales.
    Todos los perros son mamíferos.
  2. Algunas palabras son verbos.
    Todos los números son palabras.
    Todos los números son verbos.
Aquí tenemos dos razonamientos que siguen exactamente la misma estructura y en ambas las premisas son verdaderas.
En el primer caso, la conclusión es verdadera. ¿Alcanza eso para afirmar que la estructura es válida? Ya hemos dicho que no. Miremos el segundo caso: la conclusión es falsa.
Entonces podemos concluir que el razonamiento "Algunos A son B y todos los C son A; entonces todos los C son B" es inválido. Podemos dar con una conclusión cierta pero también podríamos llegar a una falsa.

Ahora la pregunta del millón... ¿Cómo haremos para saber si un razonamiento es válido o no? Usaremos un método práctico y sencillo: las tablas de verdad.
Lo que haremos será plantear las proposiciones en una tabla y combinar los valores de verdad posibles de todas las formas. Es decir, si tenemos dos proposiciones (p y q) formaremos las siguientes combinaciones de valores (donde V es verdadero y F, falso):
  1. p = V; q = F
  2. p = V; q = V
  3. p = F; q = F
  4. p = F; q = V

¡A ponerlo en práctica!
La mejor manera de entender esto, es ponerlo en práctica. Vamos a evaluar la siguiente deducción: "Miguel tiene fiebre si su temperatura corporal supera los 38ºC. Actualmente su temperatura es inferior a 38ºC, por lo tanto no tiene fiebre".
Primero que nada, vamos a pasarla al lenguaje simbólico:
Diccionario
p: Miguel tiene fiebre.
q: La temperatura corporal de Miguel supera los 38ºC.

[(q -> p) ^ ~q] -> ~p

Nota: Los operadores lógicos son binarios, es decir que unen las proposiciones de a dos. Por lo tanto, usamos los paréntesis y corchetes para estar completamente seguros de que no se malinterprete.
Recomiendo releer las explicaciones de cada operador y luego ver el apéndice donde se incluyen las tablas de verdad de cada uno para entender cómo se completa en el ejemplo siguiente.
Planteamos la tabla y completamos los posibles valores para p y q. Han de ser coherentes entre ellos, es decir, si en un renglón p es verdadera, ha de serlo todas las veces que aparezca en ese renglón.
En el paso siguiente, vamos a completar los cuadros de negación. Recordemos que ha de ser contrario al valor que tenía originalmente la proposición. Habría de quedar algo así:
Ahora sigamos el orden: vamos a completar el condicional que está dentro del paréntesis:
Luego la primer conjunción (que esá entre corchetes). Recordemos que debemos comparar el cuadro del condicional con el de la negación:
Por último, habremos de comparar la columna que acabamos de completar con la última negación. Así estaremos operando con el segundo condicional:
¡Listo! Ahora, ¿cómo sabemos si la forma de razonar es válida o no mirando la tabla? Enfoquen la vista en la columna verde (que es el resultado final de todas las operaciones). Si todos los cuadros tienen una V, se dice que el razonamiento es una tautología y significa que es válido en todos los casos, sin importar cuales sean las proposiciones.
Pero... A nosotros no nos ha quedado una tautología, ¿cierto? Porque hay un cuadro que tiene una F. ¡Muy bien! Esto se llama verdad contingente y quiere decir que el modo de razonar no es válido por sí mismo.
Un tercer caso podría ser que todas fueran "F"s. En ese caso se le llama contradicción y muestra que cualquier razonamiento que tenga esa forma, será inválido.

Veamos un segundo ejemplo: "Miguel tiene fiebre si su temperatura corporal supera los 38ºC. Actualmente no tiene fiebre, por lo tanto su temperatura corporal no supera los 38ºC".
Diccionario
p: Miguel tiene fiebre.
q: La temperatura corporal de Miguel supera los 38ºC.

[(q -> p) ^ ~p] -> ~q

Veamos la tabla:
"¡Es una tautología!" ¡Exacto! En realidad tiene una forma lógica preestablecida que se llama "Modus Tollendo Tollens" y es cierta siempre.
Básicamente nos dice que partiendo de un condicional, la negación del consecuente equivale a la negación del antecedente.
Recordatorio: Antecedente es la parte del condicional que va antes de la flecha y consecuente la que va después.

Conclusión
Un razonamiento es válido independientemente de toda variable si y sólo si todos los cuadros de la tabla (en la columna final) resultan verdaderos.


Parte II: Scripting

Estructuras condicionales I: YES/NO BOX


¡El jugador puede tomar decisiones!
Con todo lo que hemos avanzado en la parte teórica de la lógica, dimos los primeros pasos para plantear estructuras condicionales. La forma más simple de hacerlo es con la famosa cajita que todos los que hayan jugado pokémon conocen; esa que nos permite tomar una decisión muy importante... ¡Decir que sí o que no!
Hoy vamos a preparar un script similar al anterior pero con una diferencia: plantearemos una estructura condicional en la que podremos elegir sí o no.
Paso a enunciar el programa que queremos lograr: "Hablamos con un NPC que nos de a elegir entre si queremos que nos salude o no. En caso de respuesta positiva, dirá "¡Hola! Un gusto saludarte."; si negamos, no dirá nada.
Ahora lo escribiré en lenguaje simbólico.
Diccionario
p: Quiero que me salude.
q: Me saluda.

p -> q

Muy bien, la cosa es simple: si le decimos que sí, saluda. No hay ninguna otra condición y por tanto no aparecerán más operaciones lógicas.

Scripteando
Código:
#include "stdlib/std.rbh"

#define dijo_no 0

#dyn 0x800000
#org @inicio
lock
faceplayer
loadptr :preguntasaludo
callstd 5
compare LASTRESULT 1
if dijo_no goto :noquiero

:siquiero
loadpointer [MENTION=37310]Sal[/MENTION]udomensaje
callstd 6

:noquiero
release
end

:preguntasaludo
= ¿Quieres que te salude?$$

#org [MENTION=37310]Sal[/MENTION]udomensaje
= ¡Hola!
= Un gusto saludarte.$$

Script en el RA (imagen)





Vamos a ir por partes porque aquí he hecho muchas cosas nuevas que, si no lo toman con calma y paciencia, no entenderán. Alternativamente, para aquellos que necesiten ir un poco más lento dejaré debajo un spoiler con el script escrito de una forma más directa.

#include "stdlib/std.rbh"
(...)
#dyn 0x800000
#org @inicio
lock
Hasta aquí (excluyendo el define que no he escrito) no deberían tener problemas en entenderlo. Ya lo expliqué en la parte anterior del tutorial.

faceplayer
Bueno, este comando lo que hace es que el npc al que le hablas se voltee hacia tu lado porque... Bueno, cuando hablamos con alguien nos giramos para quedar de frente.

loadptr :preguntasaludo
callstd 5
(...)

loadpointer [MENTION=37310]Sal[/MENTION]udomensaje
callstd 6
Si entendieron el otro script, se habrán dado cuenta que aquí lo que hacemos es mostrar dos mensajes en pantalla (uno para preguntar y el otro es el saludo luego de haber dicho que sí).
¡Alto ahí! En el primero no dice "loadpointer" sino "loadptr", ¿por qué? Son exactamente lo mismo. De hecho, "loadptr" es un alias, otra forma de llamar al comando loadpointer. En resumen, si queremos ahorrarnos milésimas de segundos para escribirlo abreviado, podemos usar loadptr que es lo mismo.
¿Y por qué "preguntasaludo" tiene un ":" y no un "@"? Porque podemos. Ya explicaré la diferencia cuando hable de :siquiero y :noquiero. Digamos que su función es similar a la del @, indica un "offset dinámico".
¿Y por qué un callstd 5 en lugar del 2 que usamos antes? ¡Sencillo! La función predefinida en el standard 5 es, precisamente, la que abre la caja con las opciones "YES" y "NO". Para más información revisen el apéndice de comandos.
¿Y luego por qué un callstd 6? Porque ya usamos el faceplayer antes, entonces no necesitamos el standard 2 que viene con eso incluído sino el 6 (que no lo trae).
¿Y si quiero ponerle 2? Da igual, sólo están incluyendo una operación extra e innecesaria en el procesador. No pasará nada, funcionará.

#define dijo_no 0
¡Llegamos! Esta es una parte muy interesante, ya veremos más adelante que nos ayuda mucho. Básicamente nos permite darle un valor a una palabra.
En este ejemplo, le pusimos el valor "0" a la expresión "dijo_no". Entonces, cada vez que escribamos más abajo dijo_no, lo cambiará por un 0 al compilar.
Nota: El número puede estar en decimal o en hexadecimal, da igual.

compare LASTRESULT 1
if dijo_no goto :noquiero
El compare es un comando que explicaré en detalle cuando veamos las variables. Por ahora, digamos que compara dos valores.
LASTRESULT es una variable que almacena (como su nombre lo indica) el último resultado.
Entonces, comparamos el resultado del msgbox con el número 1. Recordemos que los "resultados" son siempre valores numéricos.
Como expliqué recién, ese dijo_no equivale a un 0. Eso quiere decir que es lo mismo escribir if 0x0 goto :noquiero.
"if" significa "si" y es lo que marca nuestra estructura condicional. Comprueba una condición.
En este caso lo que nos fijamos es si la variable LASTRESULT tiene un valor menor a 1. Si lo tiene, significa que el usuario respondió negativamente. En caso contrario, el valor será exactamente 1.
Ahora pasemos al goto. Del inglés "go to" significa "ir a". Al estar después de un "if", lo que hace es saltar al offset :noquiero si y sólo si la condición es veradera. En este caso, si el valor efectivamente es menor que 1.

:siquiero
(...)
Bueno, esto me sirve para explicar la principal diferencia entre las etiquetas dinámicas con : y las que tienen @. Digamos que al usar : se compila en el lugar siguiente. Es decir, siempre seguirá ejecutándose allí. En cambio, al usar el @ lo compila donde haya lugar suficiente, no necesariamente seguido de lo anterior.

Ejemplo explicativo


Entonces debemos tener cuidado porque si hiciéramos esto:

#org @inicio
lock

:mensaje
loadpointer @texto
callstd 2

(etc)
Funcionaría, porque siempre luego del lock se ejecutaría el loadpointer.

En cambio, si lo pusiéramos así:

#org @inicio
lock

#org @mensaje
loadpointer @texto
callstd 2

(etc)
Podría suceder que funcione si el compilador encuentra espacio vació junto y quiere compilarlo allí. Pero es altamente probable que no lo haga. En ese caso, quedaríamos atrapados en un bucle sin fin pues el script no terminaría nunca y el juego se congelaría.
Recuerden, si usan @ de este modo, deben agregar un goto.

Dicho por Ejemplo
#org @inicio
lock
goto @mensaje

#org @mensaje
loadpointer @texto
callstd 2

(etc)


:noquiero
release
end
Bueno, estos comandos ya están explicados. Libera al NPC y termina el script.
Notemos que noquiero también es una inline label (que lleva :). Eso quiere decir que, si le dices que te salude, luego de hacerlo continuará ejecutando esta parte.
Entonces, usamos los mismos release y end tanto si saluda como si no. Es una forma efectiva de ahorrar espacio.

:preguntasaludo
= ¿Quieres que te salude?$$
Bueno, para los textos también podemos usar los :.
En cuanto al $$ del final, es para indicarle que allí termina el texto. De no ponerlo, podría ocurrir algún problema con el texto que intentaría seguir leyendo a continuación bytes que no corresponden.

#org @Saludomensaje
= ¡Hola!
= Un gusto saludarte.$$
Otra cosa que no había mostrado hasta ahora es la capacidad de poder ecribir el mensaje en varias líneas de texto.
Para eso sólo debemos comenzar cada renglón con un "=".
Pero notemos una cosa: esto no significa que haga el salto de línea ingame. Para eso debemos aclararlo con el carácter "\n".
Dicho por Ejemplo
= ¡Hola!\n
= Saludo segunda línea.
Bueno, compilamos el script y lo asignamos a un mini tal como lo expliqué en la sección anterior y... voilà, sólo falta probarlo...

Script simplificado


Código:
#include "stdlib/std.rbh"

#dyn 0x800000
#org @inicio
lock
loadptr :preguntasaludo
callstd 5
compare LASTRESULT 1
if 0 goto :noquiero
loadpointer [MENTION=37310]Sal[/MENTION]udomensaje 'Esto se ejecuta si dices que sí
callstd 6

:noquiero
release
end

:preguntasaludo
= ¿Quieres que te salude?$$

#org [MENTION=37310]Sal[/MENTION]udomensaje
= ¡Hola! Un gusto saludarte.$$
Aquí he dejado las inline labels (etiquetas dinámicas con :) porque, aunque no lo crean, son más que útiles y un rasgo distintivo del RA. Esfuércense por comprenderlas. Si no pueden, pregunten, ¡no muerdo! mientras explico



Sólo quiero decir que este capítulo va dedicado a mis amigos @Dark Miutu y @Evird que les encanta ir por la vida sin saludar.

Nota importante: La versión del RA en el post principal ha sido actualizada a la más nueva (del 17 de enero). A quien tenga la antigua, recomiendo descargar esta que soluciona un problema pequeño con el faceplayer.
VISCA CATALUNYA!
LLIBERTAT


"Lluitem pacíficament, perquè és l'essència de la llibertat. Dubtem com qualsevol persona, perquè és l'antídot a la temeritat. Parlem com a demòcrates, perquè és la clau de l'èxit. Confiem els uns amb els altres, perquè serà la derrota dels tirans"
  #9  
31/01/2018
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Espectacular tutorial sin duda. Muy bien detallado, conciso y "provoca" utilizar la herramienta de @cosarara97

Desde luego desde que la conocí con una duda recorriendo variables, su uso es muy parecido a los compiladores de programacion y eso me hizo progresar y hacer incluso mas facilmente scripts que se me dificultaban

Chapeau.
  #10  
01/02/2018
Predeterminado Respuesta: [MACROTUTORIAL] Scripting desde cero y hasta el infinito
Ahhhhh amo esta cosaaaa!!!!
justolo que necesitaba me parece muy bien explicado, simplemente hermozho ;_;
"Mirar hacia arriba a alguien es querer ser como el algún día, pero yo soy impertinente".


"Un pingüino nunca podrá volar, no importa cuanto lo intente".
Respuesta

Herramientas
Desplegado

Permisos para publicar mensajes
No puedes crear nuevos temas
No puedes responder mensajes
No puedes subir archivos adjuntos
No puedes editar tus mensajes

Los BB code están Activado
Los Emoticones están Activado
El código [IMG] está Activado
El Código HTML está Desactivado
Trackbacks are Activado
Pingbacks are Activado
Refbacks are Desactivado



Extra
Estilo clásico
La franja horaria es GMT +1. Ahora son las 04:57.