Recipientes, superficies y sub-objetosEl modelo del mundoEl modelo del mundoLugares, decorado, salidas y mapa

Lugares, decorado, salidas y mapa

Volviendo a "Ruinas" ¿a dónde llevan los escalones? Vamos a añadir cuatro habitaciones más, conectadas entre sí de la forma siguiente:

   Camara -- Telaraña  
     |
  Corredor
     |
  Sepulcro

estando la Camara debajo del Bosque en el que hemos empezado. Por ejemplo, aquí tenemos la definición de la Camara:

Object  Camara "Cámara Cuadrada"
 with   irrelevante 'dintel' 'dinteles' 'este' 'sur',
        descripcion "Una hundida y tenebrosa cámara de piedra, de 10 
            metros de ancho. Un haz de luz corta las tinieblas desde 
            lo alto de las escaleras, dándole a la cámara una 
            iluminación difusa. Pero en las sombras, dos bajos 
            dinteles al este y al sur llevan a la oscuridad más 
            profunda del Templo.",
        arriba Bosque,
        al_e   Telarania,
        al_s   Corredor,
 has    luz;

Lo mismo que al Bosque, hay que darle a esta habitación el atributo luz (aunque la descripción diga que está casi a oscuras). Si no le diéramos este atributo, el jugador no podría ver absolutamente nada (ni siquiera la descripción), a menos que llevara consigo una linterna o lámpara de algún tipo, lo cual es imposible de momento ya que no ha podido encontrar ninguna en el juego.

Por otro lado, esta habitación tiene una propiedad nueva, llamada 'irrelevante', que contiene una lista de las palabras a las que "no es necesario referirse en este juego". Si el jugador intenta hacer cualquier cosa con el 'dintel', recibirá la respuesta estándar "Eso no es importante para el juego". Si no hubiéramos puesto la propiedad irrelevante, la respuesta habría sido "No veo eso que dices" (que es la respuesta de Inform cuando el jugador intenta usar un objeto que el juego desconoce, como por ejemplo: "EXAMINA ASDFG". A algunos jugadores les molesta que el juego responda "Eso no es importante", si quisiéramos proporcionar una descripción a los dinteles bastaría codificarlos como un objeto dentro de esta habitación pero ya que no vamos a tomarnos ese trabajo, lo correcto es poner esas palabras en la lista irrelevante, de lo contrario quedaría muy mal que en la descripción de la habitación se mencionen unos dinteles y después, cuando el jugador intenta examinarlos le dice que "No veo eso que dices" ¡Pero si los había mencionado hace un momento! Notar también que en la lista de palabras irrelevantes aparece 'sur' y 'este'. Esto es para permitir que el jugador haga referencias al 'dintel sur' y al 'dintel este'. No interferirá con el verbo 'sur' o 'este' que le serviría para caminar en esas direcciones.

(Esp) La propiedad irrelevante existe sólo en la librería InformATE, y no existía en la original. La librería original usaba la propiedad nombre, aprovechando que en las habitaciones esta propiedad no era utilizada (ya que un jugador no puede usar ningún verbo sobre una habitación, ésta no necesita una lista de nombres). No obstante, pensé que usar nombre para esto podría dar lugar a confusión, por lo que rebauticé esta propiedad como irrelevante.
(!) No obstante, internamente la propiedad irrelevante se convierte en la propiedad nombre, ya que es un alias de ella. Esto significa que si el programador lo prefiere puede usar el convenio original y poner la lista de palabras irrelevantes bajo la propiedad nombre de la habitación. También significa que no se puede intentar usar a la vez la propiedad nombre y la propiedad irrelevante, ya que ambas son en realidad la misma.

Vamos a poner algún elemento dentro de la habitación: unas inscripciones. Esto será un objeto más (ya hemos dicho que para Inform todo son objetos), con la particularidad de que el jugador sólo podrá examinarlo, pero no cogerlo:

Object "bajorrelieves" Camara
 with   nombre 'bajorrelieves' 'bajorrelieve' 'marcas' 'inscripcion'
            'inscripciones' 'simbolos' 'grabado' 'grabados' 'pared'
            'paredes' 'suelo' 'techo',
        inicial "El suelo, el techo y las paredes están cuajados de
            inscripciones en bajorrelieve.",
        descripcion "Cuando miras a las inscripciones de cerca,
            parecen estar inmóviles, pero tienes la desagradable
            sensación de que cuando no las miras están deslizándose,
            reptando. No tienes idea de su significado.",
 has    estatico nombreplural;

El atributo estatico indica que este objeto no se puede mover (lo cual prohibe algunos verbos al jugador, como "coger", "empujar", "tirar de", aunque le permite otros como "examinar", "tocar", "escuchar").

(Esp) El atributo nombreplural indica que el "título" de este objeto (la palabra entre comillas en la cabecera del mismo, es decir "bajorrelieves") es una palabra que está en plural. Esto necesita conocerlo la librería para poner delante el artículo correcto (que será "los" o "unos", según el caso).

(!) En este objeto la lista de sinónimos mezcla de nuevo palabras en masculino, femenino, singular y plural, como en el ejemplo de los escalones. Si quisiéramos ser rigurosos y separar esta lista de sinónimos en varias listas según su género y número, la definición de este objeto debería ser:

Object "bajorrelieves" Camara
 with   nombre_m 'bajorrelieve' 'inscripcion' 'simbolo' 'grabado'
            'techo' 'suelo',
        nombre_f 'pared',
        nombre_mp 'bajorrelieves' 'inscripciones' 'simbolos' 'grabados',
        nombre_fp 'marcas' 'paredes',
        inicial "El suelo, el techo y las paredes están cuajados de
            inscripciones en bajorrelieve.",
        descripcion "Cuando miras a las inscripciones de cerca,
            parecen estar inmóviles, pero tienes la desagradable
            sensación de que cuando no las miras están deslizándose,
            reptando. No tienes idea de su significado.",
        genero G_MASCULINO+G_PLURAL,
 has    estatico;

Observar que ahora el género y número ya no se especificaría con el atributo nombreplural, sino con la propiedad genero.

En la descripción de la cámara cuadrada mencionamos un haz de luz. Es probable que el jugador intente examinarlo o hacer algo con él, por lo que o bien lo ponemos en la lista irrelevante de la habitación (y entonces la respuesta ante cualquier acción del jugador sería "Eso no es importante para el juego"), o bien creamos este objeto en la cámara para dar nuestras propias respuestas:

Object haz_de_luz "haz de luz" Camara
 with   nombre 'haz' 'luminoso' 'luz' 'rayo' 'rayos' 'sol',
        descripcion "El haz de luz crea destellos en las motas de
            polvo del aire, lo que le hace parecer casi sólido.",
 has    escenario;

El atributo escenario que tiene este objeto le convierte automáticamente en estatico (el jugador no podrá cogerlo ni empujarlo, etc) y además impedirá que sea mencionado en la descripción de la habitación ("Aquí puedes ver un haz de luz"). Este objeto está en la habitación, pero no se menciona. El jugador puede referirse a él y tratar de hacer cosas, pero sólo se le permitirá examinarlo. Ante otra acción como "Empujar" o "Coger" la librería dará la respuesta estándar "No eres capaz." o "Difícilmente podrías llevarte eso.", respectivamente). Un perfeccionista añadiría además la siguiente regla al rayo de luz:

 antes [;
    Examinar, BuscarEn: rfalse;
    default: "Sólo es un haz de luz inmaterial.";
],

gracias a lo cual el haz podría ser examinado (y también buscado o mirado a través) pero ante cualquier otra acción saldría el mensaje "Sólo es un haz de luz inmaterial.". La palabra default significa "cualquier otra acción que no haya sido mencionada en las líneas anteriores".

Si has añadido el código anterior al juego y lo has intentado compilar, te habrás sentido frustrado. Inform no puede aún completar el juego porque faltan las habitaciones Telarania y Corredor, que se mencionan en las salidas de la Camara. De momento, puedes añadir las siguientes líneas al código:

Object Telarania;
Object Corredor;

Por supuesto esto está sin terminar, pero de este modo al menos Inform ya admitirá tu código fuente y creará el correspondiente juego que podrás probar con Frotz. Y si lo haces encontrarás una nueva frustración. Estamos en el bosque, vemos los escalones y la seta, pero no podemos bajar por la escalera para llegar a la cámara ¿por qué?

La respuesta es que, si bien los escalones tienen una propiedad llamada puerta_a que dice llevar a la cámara, para que esto realmente funcione el Bosque también tendría que tener una salida hacia abajo que lleve a dicha cámara. Si vuelves a mirar el código del Bosque verás que no hay ninguna salida definida, por lo que no se puede abandonar este lugar por más que haya unos escalones que parezcan llevar a otro sitio. La solución es simple: añadir la propiedad abajo en el bosque, que lleve a la cámara (en rigor, habría que hacer que la propiedad abajo llevara a los escalones, pero de momento para no liar más la cosa lo dejaremos así).

(!) Aparentemente esta repetición no parece tener sentido. En los escalones hay una propiedad llamada puerta_a que lleva a la cámara. Por otro lado los escalones tienen también una propiedad llamada direcc_puerta que dice abajo ¿y ahora resulta que hay que añadir una propiedad abajo en el Bosque que lleve a la cámara o a los propios escalones? ¿No es esto es un poco retorcido? En efecto, lo es, pero es la forma de hacer las puertas en Inform y la razón de este retorcimiento es que el jugador pueda escribir "VETE ABAJO", "BAJA" o "VETE A LOS ESCALONES" y que en cualquier caso la acción generada sea la misma: <Ir abajo>. Además eso nos permitirá programar una rutina en los escalones que reaccionen cuando el jugador trate de atravesarlos. Esto quedará más claro (¡esperemos!) en la sección sobre puertas.

Ya que vamos a modificar el Bosque para añadirle una salida hacia abajo, aprovecharemos para añadirle mensajes si el jugador intenta salir en cualquier otra dirección. Simplemente modifica el bosque para que quede así:

Object  Bosque "Bosque Sombrío"
 with   descripcion
            "En este diminuto claro, la alfombra de agujas de pino 
            se ve rota por unos escalones tallados en piedra que 
            descienden hacia la oscuridad. Los oscuros árboles 
            crecen por todos los lados, el aire está húmedo por la 
            lluvia reciente, las lianas cuelgan en el aire.",
        abajo Camara,
        arriba "Los árboles son muy espinosos y te hieres las manos
            tratando de subir por ellos.",
        no_puedes_ir "La selva tropical es densa, y no has estado 
            luchando todos estos días para abandonar ahora tu 
            descubrimiento. Realmente necesitas antes encontrar unos 
            cuantos objetos de valor para justificar el abandono de 
            la expedición.",
 has    luz;

La propiedad abajo llevará ahora a la cámara. La propiedad arriba contiene una cadena de texto que se mostrará al jugador cuando intente la orden "SUBE" o "VETE ARRIBA", "ARRIBA" o simplemente "U" (abreviatura del inglés "UP"). En general, definir una de las salidas como una cadena de texto implica que el jugador no puede ir en esa dirección y que se le mostrará el mensaje que hayamos escrito en la cadena. Finalmente, la propiedad no_puedes_ir se mostrará cuando el jugador intente salir en cualquier otra dirección que no haya sido definida para esa habitación (reemplazando así el mensaje por defecto "No puedes ir por ahí.")

Al igual que en otras propiedades, en vez de dar simplemente un texto puedes dar una rutina para la propiedada arriba o cualquier otra dirección, que imprima texto diferente según las circunstancias. Es necesaria una propiedad no_puedes_ir en el bosque porque en la vida real uno puede ir en la dirección que quiera cuando está en un bosque; lo que hacemos aquí es explicar al jugador las reglas del juego: vete abajo, encuentra algún tesoro arcaico, y después puedes acabar el juego.

Las habitaciones también pueden tener una rutina antes, y despues que se aplicarían a todas las acciones que tengan lugar en esa habitación. Por ejemplo, podemos hacer que pase algo especial si el jugador pone la seta bajo el haz de luz, añadiendo la siguiente regla en la Camara:

  antes [;
         Meter, PonerA, PonerSobre:
            if (uno==seta && otro==haz_de_luz)
            {
                remove seta;
                "Dejas la seta en el suelo, bajo el resplandor del haz   
                luminoso. Empieza a burbujear de forma obscena, se
                hincha y finalmente explota en un millar de diminutos
                insectos que corren en todas direcciones buscando la
                oscuridad. Sólo quedan migajas del hongo.";
            }
        ],

Observa que capturamos tres acciones diferentes: Meter, PonerA y PonerSobre, de este modo admitimos muchas de las frases que podría intentar el jugador. La acción Meter es generada si el jugador intenta PON LA SETA DENTRO DE LA LUZ o METE LA SETA EN LA LUZ, la acción PonerA es generada si el jugador intenta PON LA SETA A LA LUZ y finalmente la acción PonerSobre sería generada si el jugador pone PON LA SETA EN LA LUZ. Sin embargo, si pusiera PON LA SETA BAJO LA LUZ no sería comprendido. La librería no tiene definido el verbo "poner cosas debajo de cosas". No obstante sería posible definir ese verbo de forma bastante sencilla, como ya veremos.

En realidad la regla anterior podría haberse escrito como parte de la seta, o como parte del haz de luz. Las propiedades antes y despues de las habitaciones se usan más bien para excepciones concretas que sólo tienen lugar en ciertas habitaciones.

(!)(!) A veces la habitación puede cambiar una vez que la acción ha tenido lugar. Por ejemplo, la acción Ir es notificada a la rutina antes de la habitación que va a ser abandonada, pero es notificada a la rutina despues de la habitación a la que se ha llegado una vez que la librería ha efectuado el movimiento. Por ejemplo:

  despues [;
     Ir:  if (uno == obj_abajo) 
          print "Te sientes al borde de un gran descubrimiento...^";  
  ],

haría aparecer esa frase cuando el jugador entrara en la habitación a través de la dirección "abajo" de la habitación anterior. Observar que en este caso hemos usado el comando print y no print_ret (o su equivalente, la cadena entre comillas a secas). Esto significa que se imprime el mensaje pero no se retorna true. La ejecución continuaría en la línea siguiente, que es ya el fin de la rutina, por lo que esta terminará de todas formas, pero retornando false en este caso. De este modo la librería seguirá con su curso de acción normal, que consistirá en imprimir la descripción de la habitación a la que entramos.

Algunos objetos pueden estar en varias habitaciones al mismo tiempo. Por ejemplo, en "Ruinas" hay una niebla olorosa:

Object niebla_olorosa "niebla baja"
 with   nombre 'niebla' 'neblina',
        adjetivos 'baja' 'enroscada',
        inicial "Una niebla baja se enrosca a tus pies.",
        descripcion "La niebla transporta un rico aroma a caldo.",
        esta_en Camara Bosque,
        antes [;
         Examinar, BuscarEn: rfalse;
         Oler: <<Examinar self>>;
         default: "La niebla es demasiado inmaterial";
        ],
 has    estatico femenino;

La propiedad esta_en da una lista de los lugares en los que se encontrará este objeto (en este ejemplo sería la Cámara subterránea y el bosque). Observar que por tanto no hay que poner en la cabecera del objeto su localización. Observar también cómo en la regla antes se devuelve false para las acciones Examinar y BuscarEn (con lo que para estas acciones la librería se ocupará de todo), mientras que para la acción Oler se genera una acción Examinar (lo que causará que se muestre de nuevo la descripción), y para cualquier otra acción mostramos el mensaje "La niebla es demasiado inmaterial." (y automáticamente se retorna true con lo que la librería ya o hará nada más. El atributo estatico casi sería sobrante en este caso, ya que la niebla no puede ser movida de todas formas porque lo impedimos en la regla antes.

(!) Si el juego contuviese muchas habitaciones con niebla, podría llegar a hacerse pesado escribir los nombres de todas ellas en la propiedad esta_en de la niebla. Más difícil aún sería modificar esta lista durante el juego si la niebla se moviera por entre las diferentes habitaciones. Por suerte, esta_en puede ser una rutina en vez de una lista de lugares. La librería llamará cuando lo necesite a esta rutina para preguntarle al objeto ¿estás en esta habitación? y el objeto debe responder true o false. La habitación por la que se le está preguntando se halla almacenada en la variable localizacion. Por ejemplo, supongamos que queremos crear el objeto "sol", que esté visible en todas las habitaciones del juego (excepto en las que se hallan a oscuras). Bastaría con programarlo en la forma siguiente:

Object sol "sol",
 with  nombre 'sol',
       descripcion "Desde donde estás puedes ver al sol brillando con
              fuerza." 
       esta_en [;
           if (localizacion has luz) rtrue;
       ],
 has  escenario;

(!)(!) La propiedad esta_en sólo es consultada cuando el jugador entra en la habitación. Si lo que necesitas es hacer aparecer o desaparecer un objeto en un momento dado (mientras el jugador sigue en la misma habitación), cambiar la propiedad esta_en no funcionaría. En este caso tendrías que programar "a mano" el movimiento del objeto.

Si necesitas hacer desaparecer la niebla del juego para siempre, deberás ejecutar el código:

  remove niebla_olorosa;
  give niebla_olorosa ausente;    

La primera línea elimina la niebla de cualquier habitación en la que estuviera (digamos que pone la niebla en un lugar fuera del juego). La segunda línea le otorga el atributo ausente que sirve para que la librería ignore en lo sucesivo la propiedad esta_en de ese objeto (si no lo hiciéramos, la librería volvería a traer de vuelta a la niebla tan pronto como el jugador entrara en una habitación de las listadas en esta_en).

Algunos objetos alteran la percepción del jugador en algunos sentidos. Por ejemplo, la niebla huele a caldo, lo que significa que si el jugador pone "Huele" en una habitación en la que hay niebla, debería ser informado del olor a caldo. Para esto es ideal la rutina reaccionar_antes que se añadiría al objeto niebla_olorosa como sigue:

  reaccionar_antes [;
      Oler: if (uno == 0) <<Oler niebla_olorosa>>;
  ],

Esta rutina se llama "reaccionar" porque la niebla reacciona al hecho de que la acción Oler está teniendo lugar cerca. La variable uno se compara con cero para ver si el jugador ha escrito simplemente "HUELE", y no por ejemplo "HUELE LA SETA". Así, cuando la acción Oler tiene lugar cerca de la niebla, se convierte en Oler niebla_olorosa; mientras que la acción Oler seta se dejaría en paz.

Los cinco sentidos tienen acciones asociadas en InformATE: Mirar, Escuchar, Oler, Tocar y Probar. La acción Mirar nunca lleva un nombre asociado (la orden "mira cosa" se convierte en la acción Examinar). Escuchar y Oler pueden llevar un nombre asociado o no. Tocar y Probar siempre llevarán un nombre asociado.

(?) EJERCICIO 5  Escribir el código de una niebla naranja que, cuando aparezca en el juego impedirá al jugador la visión y el movimiento (no podrá mirar la habitación, aunque sí examinar los objetos que lleve consigo, y no podrá caminar en ninguna dirección ni salirse de la niebla). Pista: la niebla debe reaccionar ante estas acciones, impidiéndolas.
(Solución)

Los puntos cardinales en Inform son objetos, llamados obj_n, obj_s, etc. (y también existen obj_adentro, obj_afuera, obj_arriba y obj_abajo. No debes confundir los puntos cardinales (que son objetos) con las propiedades que representan las salidas de una habitación, llamadas al_n, al_s, etc. (y también adentro, afuera, arriba y abajo). Recuerda:

De hecho puedes cambiar completamente los nombres de las direcciones. En lo que respecta a Inform, una dirección no es más que un objeto que se encuentra dentro del objeto predefinido Brujula, así que si cambias la definición de estos objetos puedes cambiar los nombres de las direcciones.

(?) EJERCICIO 6  En primer milenio antes de cristo los pueblos Mayas de la península del Yucatán usaban colores para referirse a los puntos cardinales, así tenían el blanco (yac) para el norte, rojo (chac) para el este, amarillo (kan) para el sur y negro (chikin) para el oeste. ¿cómo haríamos para que un juego en Inform acepte estas palabras para referirse a los puntos cardinales? Pista: debes crear objetos con estos nombres dentro del objeto Brujula, y eliminar los objetos obj_n, obj_s, etc antiguos.
(Advertencia: la Solución es avanzada. No te preocupes si no te salió).

(?) EJERCICIO 7  ¿Cómo podríamos hacer que las direcciones este/oeste del mapa súbitamente estuvieran intercambiadas? (como ocurre en el juego "Trinity"). Sugerencia: primero escribe una rutina que se llame IntercambiaDirecciones que reciba como parámetros dos "puntos cardinales" y que intercambie las direcciones asociadas a esos puntos. Con esta función hecha el resto debería ser fácil.
(Pero la Solución a esa función no es evidente a menos que tengas muy clara la diferencia entre obj_e y al_e. Deberás mirar antes la Solución al ejercicio anterior).

(!)(!)
(?) EJERCICIO 8  En el ejercicio anterior, aunque los comandos "este" y "oeste" hayan quedado intercambiados, si las descripciones de las habitaciones mencionaban el "este" y el "oeste" esto no habrá cambiado. ¿Cómo podríamos hacer para que las descripciones también cambien cuando el mundo quede reflejado?
(No intentes adivinarlo. No tienes aún los conocimientos necesarios, pero mira la Solución si te interesa saber cómo se haría).

(!) Los objetos obj_n, obj_s, etc (los puntos cardinales) tienen una propiedad llamada cantidad, pensada para albergar un número entero (inicialmente puesta a cero), pero que la librería no usa para nada. Están pensadas para que el programador las utilice para lo que necesite (pueden ser útiles, por ejemplo, para codificar laberintos).

(!)(!) Si antes de la línea include "EParser" se define la constante SIN_DIRECCIONES, entonces la librería no creará los objetos obj_n, obj_s, obj_e, obj_o, obj_ne, obj_se, obj_no, obj_so, obj_arriba ni obj_abajo. Se espera que el programador cree otros objetos equivalentes y los coloque dentro del objeto Brujula (de lo contrario el juego sería bastante estático). Los objetos obj_adentro y obj_afuera serán creados de todas formas porque la librería los necesita para manejar las acciones de meterse y salirse en las cosas.


Zak McKraken - spinf@geocities.com

Recipientes, superficies y sub-objetosEl modelo del mundoEl modelo del mundoLugares, decorado, salidas y mapa