![]() | ![]() | ![]() | 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.
![]() |
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.
|
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").
![]() |
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 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:
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:
|
![]() ![]() |
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:
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). |
![]() ![]() |
|
![]() | 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. |
![]() | ![]() | ![]() | Lugares, decorado, salidas y mapa |