2. Vocabulario, aleatoriedad, contenedores y cerrojos

Inform 7: Empezando a programar - capítulo II

Por Xavi C.

Versión 1.2 - Revisión 16/01/11

En el anterior capítulo de este tutorial de programación en Inform 7 aprendimos a instalar el programa; también vimos cómo crear las primeras localidades de nuestro mundo de juego, además de llenarlo de objetos y decorados y poblarlo con un par de criaturas. En esta ocasión veremos más propiedades de objetos y aprenderemos a crear objetos con cerrojo. También aprenderemos a crear verbos nuevos y a ampliar el vocabulario del juego, y además utilizaremos condiciones y sucesos aleatorios para controlar lo que le pasa al jugador. ¡Vamos allá!

Cómo crear buenos puzzles

Cualquier relato interactivo debe contener algún tipo de puzzle, sea este sencillo o complicado. En la jerga habitual se le llama puzzle a cualquier obstáculo que impida avanzar normalmente en el relato, cosa que es muy importante porque dota de interactividad al texto. De otra forma, estaríamos ante un texto lineal, cuando nuestro objetivo es que el jugador haga y piense cosas para ver más cosas de nuestro relato. Antaño los relatos interactivos eran llamados comercialmente como aventuras conversacionales, y muchas de ellas son demasiado difíciles para los estándares actuales (algo que entusiasma a los más veteranos del género). La compañía más famosa que comercializó el género de las aventuras conversacionales fue Aventuras AD, y muchos de sus títulos son increíblemente difíciles, como por ejemplo en el juego del Jabato, que en su inicio debemos salir de una celda donde apenas se nos dan pistas sobre cómo conseguir salir; o en su trilogía de Cozumel, con puzzles de extrema dificultad.

Pero la realidad es que no hace falta idear complicadísimos puzzles y laberintos para ponerle las cosas difíciles al jugador, lo que de verdad importa es que los puzzles sean lógicos. Además se le deben dar pistas al jugador, para que tenga elementos con los que pensar su próximo movimiento, no queremos que deambule de aquí para allá sin saber qué hacer. También es importante ser honesto con el jugador. En los juegos antiguos sucedían este tipo de situaciones "deshonestas" especialmente injustas cuando, por ejemplo, el jugador moría al cruzar una puerta porque no sabía que tras ella había un peligro... algo que podía haberse arreglado poniendo en la descripción "una lóbrega puerta parece albergar un terrible mal" o similar, con lo que el jugador ya está advertido del peligro. Así pues, mi consejo es ser honesto y justo con el jugador, ayudándole en lo posible... ¡pero sin llegar a ofrecerle la solución!

Extendiendo el vocabulario

Pero pasemos de la teoría a la práctica y fijémonos en nuestro juego. En el capítulo anterior habíamos programado una playa y una isla, pero no habíamos creado una conexión entre ellas. Vamos a programar la opción más sencilla, que consiste en que el jugador tenga que ir a nado entre una y otra. Si vamos al juego, observaremos con perplejidad que el vocabulario de Inform no contempla el verbo "nadar", algo incomprensible teniendo en cuenta que otros verbos, como "columpiarse", sí que están contemplados. Tal vez sería mejor decir "acciones" en lugar de "verbos", pues lo anterior son acciones que el jugador puede intentar hacer. En fin, misterios de Inform. No hay problema, podemos definir nuevas acciones siempre que los necesitemos, así pues vamos a crear la acción "nadar" (copia y pega el código siguiente al final de todo):

nadaring is an action applying to nothing.

understand "nada" and "bucea" as nadaring.

report nadaring:

say "No hay suficiente agua aquí, ¿dónde he de nadar?" instead.

Aunque suene raro, como Inform está en este seudo-inglés, tenemos que crear el verbo nadaring (si te suena horriblemente raro, puedes sustituirlo por swimming o lo que te parezca, pero que acabe en ing). Con la sentencia "is an action applying to nothing" le damos a entender que el jugador puede poner simplemente "nadar", sin que tenga que referirse a nada más. Con "report nadaring" le decimos a Inform "el resultado será el siguiente", que es la frase que dice que no hay agua, seguida por un "instead"... ¿seguro?

Te explicaré algo más acerca de las acciones, pues aunque es correcto poner "instead", no lo es tanto tras un "report". Inform deja que pase el flujo del procesamiento de la acción con el siguiente orden: primero buscará la Rule, después la Check, luego la Carry out, luego la After y finalmente la Report (no te preocupes si no entiendes toda la jerga, ya la irás viendo a medida que avancemos). Como ves, Report es la última, y por lo tanto no hace falta poner el Instead, pero sí que sería necesario en la Check para detener la acción ahí, por ejemplo:

Check nadaring en something:

say "No puedo nadar en [the noun], mejor será nadar en el mar..." instead.

Para acabar de entenderlo haz la prueba, copia el siguiente código:

Check nadaring:

say "No hay suficiente agua aquí, ¿dónde he de nadar?".

report nadaring:

say "Aquí acaba la acción".

Compílalo. ¿Ves que salen las dos frases tras poner "nadar". Ahora, vamos a hacer lo mismo pero con "instead" tras el check:

Check nadaring:

say "No hay suficiente agua aquí, ¿dónde he de nadar?" instead.

report nadaring:

say "Aquí acaba la acción".

Como ves, ahora ya no sale la frase "Aquí acaba la acción", porque el "instead" ha cortado el flujo de procesamiento de la acción del que te hablé antes. Tenlo en cuenta al crear nuevas acciones, así pues sería más correcto poner:

report nadaring:

say "No hay suficiente agua aquí, ¿dónde he de nadar?".

Porque tras el report no se procesa más la acción, al ser la última de las reglas de procesamiento de acciones. Normalmente se utiliza "report" cuando creamos acciones que no hacen nada, ya que solamente pondremos un mensaje en dichas acciones, pero no tendrán más repercusión en el juego.

Nota: Puedes encontrar más información sobre las acciones y cómo las procesa Inform en los capítulos 12.7. New actions, en 12.9. Check, carry out, report, en 12.12. Check rules for actions by other people y en 12.13. Report rules for actions by other people. Recomiendo su lectura para acabar de comprender cómo ejecuta Inform las acciones y poder hacer las nuestras de forma correcta.

Verbos nuevos que requieren objetos

Bien, pero ¿y si el jugador pone: "nadar hacia la isla"? ¡Hay que contemplarlo todo! El código es más difícil, pero lo entenderás, cópialo a continuación del anterior (pero no borres el anterior, pues nos servirá igualmente):

nadaring en is an action applying to one thing.

understand "nada [something]", "nada a [something]", "nada en [something]", "nada hacia [something]", "nadia hacia la [something]", "bucea [something]", "bucea a [something]", "bucea hacia [something]" and "bucea hacia la [something]" as nadaring en.

report nadaring en something:

say "No puedo nadar en [the noun], mejor será nadar en el mar...".

Como ves, ahora le decimos que "is an action applying to one thing", con lo que el jugador ya puede nadar hacia donde quiera. También se han de escribir las otras posibles salidas, como la de bucear, nadar en, nadar a, etc. Observa que el verbo es "nadaring en", para nadar en algún lado, sino utilizaríamos el "nadaring" solo. El "something" entre corchetes es el objeto sobre el cual se intentará nadar (que en la jerga de Inform 7 se llama token), y el "report" esta vez indica al objeto con "the noun", que en el juego se verá reemplazado por el nombre del objeto en cuestión ("the noun" sería otro token). Prueba por ejemplo a "nadar en serpiente". El resultado es un poco esperpéntico, pero es un comienzo.

Nota: Si te vas a la pestaña Index y consultas la List of named values verás la lista de tokens disponibles a los que puedes hacer referencia en el código.

Verbos que no hacen ninguna acción

Fíjate en que puedes crear todos los verbos que quieras, siempre que no estén ya en la librería por defecto de Inform (se pueden modificar si se desea). Vamos a crear un verbo que no va a hacer nada en el juego, pero que el jugador puede intentar hacer... ¡rezar! Copia el código a continuación:

rezaring is an action applying to nothing.

understand "reza" as rezaring.

report rezaring:

say "¡Quiera Dios que mis plegarias sean escuchadas!".

Este era más sencillo, pero no por ello menos vistoso que el anterior, puesto que al jugador le gusta comprobar que todo lo que él ha pensado lo ha tenido en cuenta el programador. Como siempre, si te chirría ese "rezaring", puedes optar por escribirlo en inglés "praying"; esto es algo a lo que hay que acostumbrarse en Inform, aunque después de todo el jugador no tiene porqué saber cómo se ha programado todo esto.

Programando la primera vez: the first time

Continuando con lo anterior, podemos hacer que la primera vez que el jugador reza diga una plegaria de verdad, y en las siguientes diga eso de que ojalá sea escuchada, vamos a verlo:

Instead of rezaring for the first time, say "Alzas tus brazos al cielo y suplicas al altísimo: 'Padre nuestro, que estás en el cielo, santificado sea tu Nombre; venga a nosotros tu reino...'".

Así tiene más sentido y el resultado es más redondo. Nunca dejes de poner cosas así en tu juego, pues el jugador siempre puede intentar hacer cosas inverosímiles y probar de todo cuando se encuentra atascado en el juego. Observa que he puesto "for the first time" que significa "por primera vez", esto es, puedes programar una segunda, tercera y las veces que quieras, cambiando "first" por "second", "third", etc.

Cambiar de lugar al jugador

Ahora que ya sabemos cómo nadar, habrá que programar un mar por el cual hacerlo, ¿no? Empecemos por lo más sencillo, que es crear un decorado "mar" tanto en la playa como en la isla (copia el código donde corresponda, que ya sabes hacerlo):

The costa is female and scenery in Playa. The description is "Estas aguas son tan bellas como peligrosas, piensas mientras observas la diminuta isla, allá a lo lejos.". Understand "isla", "mar", "playa", "aguas", "agua", "linea" and "oceano" as the costa.

The mar is scenery in Isla. The description is "Estas aguas son tan bellas como peligrosas, piensas mientras observas la línea de la costa, allá a lo lejos.". Understand "costa", "playa", "aguas", "agua", "linea" and "oceano" as the mar.

Hay formas más ortodoxas de hacer esto, pero para empezar está bien así; hemos creado en realidad dos objetos para uno solo que es el mar, al cual hemos llamado también como costa. Fíjate en que he utilizado muchos sinónimos en los "Understand", esto es porque cuantos más sinónimos utilices, más cosas entenderá tu juego y más amigable será para el jugador, además verás que los sinónimos pueden repetirse en muchos objetos (aunque hay que tener cuidado con esto, porque si se encuentran dos objetos con un mismo sinónimo tendrás problemas de desambiguación). Pero siempre es preferible dotar a tu juego del mayor vocabulario posible.

Si lo pruebas, verás que aunque haya un mar y se pueda nadar... ¡aún no funciona nada! Esto es porque falta crear la acción que posibilitará nadar entre la playa y la isla, que la crearemos con un par de "Instead". Y no solo eso, habrá que mover efectivamente al jugador entre una y otra localidad. Esta es mi propuesta:

Instead of nadaring en the costa:

say "Decidido y sin temor te adentras en el mar, nadando hacia la lejana isla...[paragraph break]";

wait for any key;

say "Finalmente, y tras un gran esfuerzo, llegas a la costa. Contemplas maravillado la paradisíaca isla...[paragraph break]";

wait for any key;

now the player is in Isla.

Instead of nadaring in Playa, try nadaring en the costa.

Veamos si entiendes todo el código anterior, lo primero "Instead of nadaring en the costa:" significa que si el jugador intenta nadar en la costa haga lo siguiente: diga que "decidido se adentre en el mar"; se espere a la pulsación de una tecla; diga que ha llegado; se espere otra vez y, finalmente lo más importante, mueva al jugador a la isla, con ese mágico "now the player is in Isla.". También puedes conseguir algo parecido con "move the player to the Isla".

Por último, se contempla la posibilidad de que el jugador simplemente ponga "nadar" cuando esté en la playa, así que le decimos a Inform que si el jugador pone nadar entonces "try nadaring en the costa.", que significa que intente nadar en la costa. La opción de poner "try..." será una de las más utilizadas en tus futuros proyectos, puesto que te permite contemplar multitud de acciones para acabar realizando la correcta igualmente, sin tener que programarlas todas.

Estilos de texto

Los "[paragraph break]" dentro de una cadena de texto hacen que Inform separe las cadenas de texto en párrafos, lo cual quedará más estético. Si quisiéramos escribir en una línea abajo pondríamos "[line break]". Si en algún momento queremos cambiar a escribir en cursiva habrá que poner "[italic type]", si queremos usar letras en negrita, "[bold type]" (algo muy útil si queremos resaltar alguna palabra), y para volver a la tipografía normal pondremos "[roman type]".

Pruébalo todo y, siguiendo dicho ejemplo, crea la misma acción para nadar de la isla a la playa. Tendrás que utilizar un "now the player is in Playa." y un "try nadaring en the mar.", entre otras sentencias. Verás que no es tan complicado una vez sabes cómo hacerlo; aunque lógicamente hay que acabar de entender bien toda la jerga propia de Inform 7. Recuerda que siempre tienes la ayuda disponible en la pestaña "Documentation" del editor de Inform 7.

Sucesos aleatorios

Podría suceder que en el plácido mar que acabamos de crear hubieran tiburones. Es algo que podría suceder, pero no es algo seguro. Estamos hablando de un suceso aleatorio, en el que el jugador puede morir a causa de la dentellada de un escualo si por un casual pasa alguno por allí. En Inform podemos hacer uso de la aletoriedad, aunque esto a los jugadores no les guste demasiado, puesto que es algo que no pueden controlar. Pero es bueno saber que efectivamente se puede hacer, así que vamos a crear al tiburón que podría (o no) matarnos, y para ello tenemos que modificar las dos acciones de nadar anteriores, incluyendo la aletoriedad y una condición, veamos:

Instead of nadaring en the costa:

say "Decidido y sin temor te adentras en el mar, nadando hacia la lejana isla...[paragraph break]";

wait for any key;

if a random chance of 1 in 5 succeeds:

say "Pero de repente observas una amenazadora aleta de tiburón que se aproxima hacia tí... ¡intentas huir con todas tus fuerzas! Pero...[paragraph break]";

end the game in death;

otherwise:

say "Finalmente, y tras un gran esfuerzo, llegas a la costa. Contemplas maravillado la paradisíaca isla...[paragraph break]";

wait for any key;

now the player is in Isla.

Instead of nadaring en the mar:

say "Decidido y sin temor te adentras en el mar, nadando hacia la costa...[paragraph break]";

wait for any key;

if a random chance of 1 in 5 succeeds:

say "Pero de repente observas una amenazadora aleta de tiburón que se aproxima hacia tí... ¡intentas huir con todas tus fuerzas! Pero...[paragraph break]";

end the game in death;

otherwise:

say "Finalmente, y tras un gran esfuerzo, llegas a la playa...[paragraph break]";

wait for any key;

now the player is in Playa.

Como verás, he incluido una condición "if... otherwise", que como en otros lenguajes de programación es de importancia vital, se utiliza constantemente. ¡Las condiciones son muy importantes! O se hace esto, o esto otro; en este caso sirve para el suceso aleatorio del tiburón, le estamos diciendo que "si sale un 1 en una tirada aleatoria del 1 al 5 ocurrirá lo siguiente; y si no, ocurrirá lo otro, que es el "if a random chance of 1 in 5 succeeds:" y el "otherwise:". Pruébalo e intenta nadar de un lado a otro hasta que el personaje muera.

Nota: como en todo lenguaje la aleatoriedad se consigue a través de una "semilla", que se puede bloquear para que siempre salga la misma secuencia aleatoria, esto se consigue en la pestaña "Settings" clicando en "Ramdomness", aunque esto cambiará cuando hagamos la Release. Tienes más información en el capítulo 8.18. Randomness.

Como puedes ver, no ha hecho falta programar tiburón alguno, si no que ha sido cosa del azar el que aparezca, no ha hecho falta crearle un objeto "tiburon" a la condición aleatoria. Sin embargo, la aleatoriedad en este caso es bastante injusta, puesto que el jugador no tiene opción a protegerse contra el tiburón. ¿Cómo podríamos advertirle al jugador del peligro, sin matarle la primera vez? (porque dado que es un sistema aleatorio, podría ser que muriera la primera vez que nada).

Advertir al jugador de un peligro

Yo lo que haría sería indicar que en la primera travesía a nado el juego ya nos advirtiera de la presencia de tiburones, y que en la segunda vez (con el jugador ya advertido) el personaje pudiera morir aleatoriamente tal como hemos programado. Entonces habría que incluir la siguiente sentencia en nuestro código, antes que las anteriores:

Instead of nadaring en the costa for the first time:

say "Decidido y sin temor te adentras en el mar, nadando hacia la lejana isla...[paragraph break]";

wait for any key;

say "De repente, en mitad de la travesía, te das cuenta... ¡el mar está infestado de tiburones! Nadas todo lo rápido que puedes hasta llegar exhausto a la costa... ¡esta vez has tenido suerte![paragraph break]";

wait for any key;

now the player is in Isla.

Ahora el jugador ya está advertido e intentará buscar alguna otra fórmula para cruzar el mar, sin tener que ir a nado. Nos ocuparemos de ello más tarde. Fíjate en que con la misma fórmula de "for the first time:" se pueden solucionar muchas de las salidas que conducirían a un jugador a la muerte, puesto que siempre se le puede advertir en una primera vez.

Condiciones: aprender a utilizar if, then, else y otherwise

Hemos visto ya algo de las condiciones, pero profundizaremos en ellas puesto que son muy importantes. En Inform se utilizan mucho las estructuras de "if...otherwise", aunque hay más tipos. Ahora nos centraremos en los "if", puesto que con ellos se pueden resolver la mayoría de casos condicionales.

Pongamos por caso que en la playa hay un cofre. Lógicamente el cofre estará cerrado, y necesitaremos una llave ¡como no! Al abrirlo encontraremos un mapa del tesoro, y aun más, para mayor dificultad, si nadamos con el mapa a cuestas se mojará y resultará inservible. En todo lo anterior hay condiciones a mogollón. Vamos a resolverlas empezando por lo más sencillo: el cofre.

Objetos contenedores

Un objeto contenedor es aquel capaz de albergar otros objetos, como lo podría ser un cofre. En Inform hay que definir los objetos contenedores aplicándoles la clase "container". Además podrá abrirse o cerrarse, aunque inicialmente estará cerrado. Veamos su código:

The cofre is in the Playa. The description is "[if open]El pequeño cofre está abierto. Su interior es impermeable y por lo tanto el agua del mar no ha estropeado su contenido...[otherwise]Un pequeño cofre misterioso, bellamente adornado, aunque algo deteriorado por las inclemencias del mar."

It is container, openable and closed.

Rule for printing room description details of cofre:

if the cofre is open, say " abierto";

if the Playa is unvisited, say " que llega mecido por las olas, por";

if the cofre is closed and Playa is visited, say " misterioso";

omit contents in listing.

La primera parte del anterior código es la definición, descripción y las características del objeto "cofre". El cofre es un contenedor que se puede abrir pero que inicialmente está cerrado, tal como dice la sentencia "It is container, openable and closed.". Además podemos utilizar condicionales en una cadena de texto si los ponemos entre corchetes, que harán variar el texto, con un "[if open]" y un "[otherwise]" que nos ahorrarán programar un cofre abierto y otro cerrado.

La segunda parte trata de cómo percibirá el cofre el jugador dependiendo de si está abierto, cerrado o de si es la primera vez que lo ve, algo que queda más estético que si lo dejamos tal cual (haz la prueba omitiendo la segunda parte del código). Para ello nos hemos servido de una regla, una "Activity Rule", que en este caso cambiará la regla por defecto que incluye Inform. Además he puesto "unvisited", que significa "si aún no ha sido visitada", que funcionará la primera vez que el jugador llegue a la localidad. Como observarás hemos hecho un uso intensivo de los condicionales en un bloque muy pequeño, pero su importancia ha sido determinante para que todo salga bien de cara al jugador.

Llaves y candados: objetos con cerrojo

Sin embargo, no queremos hacer un cofre que se abra así, a las buenas, tenemos que crear un candado en condiciones para el cofre. Y una llave que lo abra. Así que habrá que modificar el cofre para que solo se pueda abrir con una llave específica (un puzzle clásico). Esto lo podemos resolver así (sustituye "It is container, openable and closed." por lo siguiente:

It is container, openable, lockable and locked. The matching key of the cofre is the llave oro.

Con esto le estamos diciendo a Inform que el cofre tiene un cerrojo que está inicialmente cerrado, y que solo se puede abrir con la llave de oro, gracias a las propiedades lockable y locked. Vamos a crear la llave, que inicialmente pondremos en la Isla, y ahora pruébalo todo.

A llave oro is female in Isla. The printed name of llave oro is "llave de oro". Understand "llave de oro" as llave oro.

Bien, ahora podemos meter un mapa del tesoro en el cofre:

The mapa del tesoro is in the cofre. The description is "¡Parece un mapa auténtico! Tal vez si logro descifrarlo encontraré un tesoro... ¡y seré rico! ¡Ja!".

En el siguiente capítulo

Y por el momento eso es todo, que el capítulo es bastante extenso y hemos tocado muchos temas. Ahora te toca a tí ir probando todo lo que puedes hacer con lo que has aprendido. Por ejemplo... ¿cómo harías para que el náufrago te diera la llave, tras preguntarle por el cofre? ¿cómo mejorarías al náufrago para que se diera una descripción diferente de él tras mirar la localidad? Todo esto puedes empezar a programarlo ya... tenlo por seguro, ¡sabes más de lo que crees!