Parte III - Puliendo detallesParte II: Empieza lo interesanteRompecabezas un poco más complicadosLa ventana y los barrotes

La ventana y los barrotes

¡Vamos con los últimos puzzles del juego! Ahora que el jugador tiene la funda debe descubrir el barrote suelto que hay en la ventana, atar la funda a otro barrote de la misma y salir por la ventana para terminar el juego.

¿Qué objetos participan en todo esto? Evidentemente tenemos una ventana, un barrote_suelto y un barrote_fijo, más la funda que ya teníamos. Y hay una serie de acciones que debemos contemplar para cada uno de esos objetos. De modo que vayamos por partes. Lo primero será programar la ventana.

La ventana

La ventana, de acuerdo con el modelo del mundo que tiene la librería Inform debería ser un conector que lleve de la mazmorra al exterior de la torre. Sin embargo, ya que en este juego el mero hecho de atravesar la ventana da por finalizado el juego, no necesitamos que sea realmente una conexión de esas.

De todas formas, por hacer más interesante este cursillo, crearemos una localidad nueva, el exterior de la torre, y haremos que la ventana sea un conducto que une esta localidad con la mazmorra.

Este tipo de conductos, en Inform se llaman doors (puertas), aunque como vemos no siempre son una puerta real. Son simplemente un objeto que comunica dos lugares. También podría ser un puente, una cabina de teletransportación, ...

Las puertas tienen dos propiedades importantes:

Además, debe tener el atributo door, para indicarle a Inform que es un conector entre dos lugares, y que por tanto debe admitir por defecto la orden ENTRAR EN o METERSE POR ese objeto.

Vamos a programar una versión inicial de la ventana, sin puzzle alguno, que deje salir al jugador de la mazmorra. En primer lugar hay que añadir a la mazmorra una nueva salida, pero en lugar de indicar a dónde lleva esa salida (llevará al exterior de la torre), ponemos qué "puerta" hay en esa salida, que en nuestro caso sería el ventanuco. La nueva mazmorra quedará así:

Object  Mazmorra "Mazmorra"
 with   description "Una silenciosa estancia débilmente alumbrada 
            por los rayos de luna que se filtran a través de un 
            pequeño ventanuco. El suelo está lleno de paja, colgando 
            de unos grilletes en la pared observas un esqueleto 
            humano.",
        e_to EscaleraCaracol2,
        out_to ventanuco,
 has    light;

La dirección es out_to (afuera), y por tanto se activará cuando el jugador escriba "SALIR", o "AFUERA" (esta es una "dirección" adicional a las clásicas NORTE, SUR, etc..)

Por su parte el ventanuco debe estar localizado en la Mazmorra, y su código sería el siguiente:

Object ventanuco "ventanuco" Mazmorra
 with   name 'ventanuco' 'ventana' 'tragaluz',
        description "A través de los barrotes de este ventanuco 
            puedes ver el exterior de la torre, iluminado por una 
            increíble luna llena.",
        door_to ExteriorTorre,
        door_dir out_to,
 has    door open scenery

Fijate que la propiedad door_to indica que esta ventana conduce a la localidad ExteriorTorre cuando se la atraviese. De modo que si el jugador está en la Mazmorra y pone "SALIR", esto es lo que ocurrirá:

Si queremos impedir que el jugador salga antes de haber reunido ciertas condiciones, podremos escribir una rutina en la salida out_to de la localidad. Esta rutina puede imprimir un mensaje y retornar true para impedir al jugador que salga, o bien retornar ventanuco para que todo prosiga normalmente. Por ejemplo, el código siguiente no dejará salir al jugador mientras no lleve consigo el carbón (es un ejemplo tonto, un ejemplo más realista lo programaremos más adelante, donde comprobaremos si el jugador ha aflojado el barrote y ha atado la funda antes de dejarle salir).

        out_to [;
            if (carbon in jugador) return ventanuco;
            else "Tienes la sensación de que olvidas algo en esta 
                  torre. Decides no irte todavía.";
        ],

Pero observa de nuevo el código del ventanuco. Además, de indicar a qué localidad lleva, es necesario poner en el ventanuco la propiedad door_dir, con el valor out_to. El por qué es necesaria esta propiedad es un poco rebuscado, y en mi opinión es un error de diseño de Inform, pero ya que las cosas están así, mejor que estés al tanto de esto. La razón es que el jugador puede, en lugar de poner SALIR, poner algo como METERSE POR LA VENTANA. Esto genera una acción Go, con noun=ventanuco, en lugar de generar Go con noun=out_obj. Si Inform se limitara a mirar la dirección door_to, y llevar al jugador al ExteriorTorre, entonces la rutina que hemos programado para impedir que el jugador pueda salir, no sería ejecutada. Dicho de otro modo, sería diferente lo que ocurre cuando el jugador pone SALIR que cuando pone METERSE POR EL VENTANUCO. Esto no estaría bien.

Por mantener una cierta coherencia, Inform quiere asegurar que siempre que el jugador se mueva de un lugar a otro, se generará una acción Go, tanto si pone SALIR, como si pone METERSE POR EL VENTANUCO. Para ello, lo que hace Inform cuando el jugador intenta meterse por una puerta, es consultar la propiedad door_dir de dicha puerta, y generar una acción Go que lleve a esa dirección. Así que en el caso de nuestro ventanuco, si el astuto jugador pone METERSE POR VENTANUCO, Inform cambiará esa acción en IR AFUERA, por lo que la rutina out_to que hemos visto antes se ejecturará y podremos impedir al jugador que salga si no cumple las condiciones que le hemos impuesto.

Clave Resumen para programar una conexión:
  • En la localidad que tiene esa conexión, una de sus salidas debe llevar a la conexión en cuestión. Por ejemplo, la salida n_to (norte) puede llevar a un Porton.
  • Hay que programar un objeto conexión (el Porton) que tenga las propiedades:
    • door_to que indica a qué localidad destino lleva la puerta
    • door_dir que indica en qué punto cardinal de la localidad origen se halla la puerta (n_to)
    y los atributos:
    • door para que Inform sepa que es una conexión, y no una localidad.
    • open para que el jugador pueda pasar
    • o bien openable (abrible) para que el jugador pueda abrirla o cerrarla (puede hacerse también que requiera una llave, pero no lo compliquemos más).
    • scenery ¡para que el jugador no pueda llevarse la conexión debajo del brazo!

Truco Las conexiones (o puertas) son ciertamente un objeto complicado en Inform. Ten en cuenta que todo lo anterior es necesario para hacer una puerta que lleve de A a B, pero ¡no al revés! Si quieres volver de B a A, ¡necesitas programar otra puerta en sentido contrario! Puede hacerse un solo objeto que lleve en ambas direcciones a la vez, pero esto exige una programación aún más retorcida. Por todo esto se ha desarrollado un módulo de ampliación, llamado "Puertas", que intenta simplificar al máximo la programación de puertas con dos lados. Si estás interesado, puedes descargar este módulo de la zona de modulos de INFSP

Los barrotes

Inicialmente, sólo estará presente el objeto "barrotes". Cuando el jugador lo examine, haremos aparecer el objeto "barrote flojo", para que el jugador pueda retirarlo. Esto planteará un problema de ambigüedad si el jugador intenta despues ROMPER BARROTE o ATAR FUNDA A BARROTE. ¿A cuál se refiere? ¿Al flojo que tiene en la mano, o a uno de los barrotes que han quedado en la ventana?

Por suerte Inform maneja las ambigüedades de forma automática. Sólo tenemos que tener la precaución de dar un nombre corto diferente a cada uno de los objetos que podrían dar lugar a confusión, y que su lista de sinónimos no sea idéntica. Así, por ejemplo, el barrote flojo puede tener como nombre "barra", y en su lista de sinónimos pondremos la palabra 'flojo' y 'floja', mientras que los restantes barrotes los llamaremos "barrotes sólidos" y pondremos las palabras 'solido' y 'fijo' en su lista de sinónimos.

Vamos a programar los barrotes sólidos, para que al ser examinados hagan aparecer el barrote flojo. Se trata del mismo truco que ya usamos para hacer aparecer el cuchillo al examinar el esqueleto, es decir, hacer uso del atributo general. Además, usamos el atributo moved del barrote flojo, para saber si el jugador lo ha retirado ya o no, y así cambiar la descripción del ventanuco.

Object barrotes "barrotes sólidos" Mazmorra
 with   name 'barrotes' 'barrote' 'solido' 'barra' 'barras'
            'solidos' 'fijos' 'fijo',
        description [;
            if (barrotes hasnt general) 
            {
                move barrote_flojo to Mazmorra;
                give barrotes general;
                "Al examinar de cerca los barrotes de la ventana,
                descubres uno que parece estar más flojo.";
            }
            if (barrote_flojo has moved) 
                "En la ventana falta un barrote. Parece que podrías
                pasar por el hueco.";
            "Entre los barrotes hay uno que parece más flojo.";
        ],
 has    pluralname scenery

Fijate que le hemos puesto el atributo scenery, para impedir que el jugador pueda coger estos barrotes. En cuanto al barrote_flojo, inicalmente estará en el limbo. Usaremos de nuevo su atributo moved para saber si el jugador lo ha retirado ya de la ventana o no, y así cambiar su descripción. En este caso no le damos el atributo scenery, para que el jugador pueda cogerlo. Pero le damos el atributo concealed, para que al MIRAR, la descripción de la localidad no diga "Puedes ver un barrote flojo". Fijate que hemos capturado algunas acciones obvias que el jugador intentará con el barrote, como moverlo, empujarlo... Y hemos cambiado tambien el mensaje por defecto para cuando el jugador lo coja.

Object barrote_flojo "barra" Limbo
 with   name 'barrote' 'flojo' 'barra' 'floja',
        description [;
            if (barrote_flojo has moved)
                "Es el barrote que quitaste del ventanuco de la 
                mazmorra.";
            "Parece que este barrote podría quitarse con un poco de
            esfuerzo.";
        ],
        before [;
         Push, Pull, Mover: "Aflojas el barrote.";
        ],
        after [;
         Take: "Con esfuerzo, consigues arrancar el barrote de su
            sitio.";
        ],
 has    female concealed;

Fíjate que le hemos puesto como nombre corto "barra", y eso nos obliga a ponerle el atributo female. De ese modo, cuando el jugador lo coja y mire su inventario, Inform le dirá que lleva "una barra", y no "un barra".

Atando cabos

Sólo queda por resolver la acción de ATAR FUNDA A BARROTE. Inform tiene ya programado el verbo ATA, y genera la acción Tie (Atar). Al igual que ocurre con Cut, el jugador puede poner simplemente ATA FUNDA, o bien ATA FUNDA A BARROTE. En el primer caso la variable second tomará el valor cero, y en el segundo tomará el valor barrote_flojo. Si pone ATA FUNDA A BARROTES, second tomará el valor barrotes.

En todos los casos, el objeto noun es la funda, y es quien recibirá la acción. Por tanto la respuesta a Tie debemos programarla en la funda, y no en los barrotes. Por tanto debemos modificar la rutina before de la funda, para tratar en ella las diferentes ataduras que se le pueden ocurrir al jugador, y dar un mensaje adecuado en cada caso. Cuando el second sean los barrotes, marcaremos el puzzle de la funda como resuelta, activando la propiedad general de la funda. Observa que no podemos usar la propiedad general de los barrotes porque esa ya la hemos usado para marcar si han sido examinados o no.

La nueva rutina before de la funda debería cambiar también la respuesta ante Take, comprobando si la funda está atada a los barrotes, ya que en ese caso el jugador no podrá cogerla. También tendriamos que manejar correctamente la acción Untie (Desatar) que no se provee en la libreria Inform (más adelante veremos cómo agregar acciones nuevas a tu juego).

Entonces la rutina before que se ocupa de todo esto podría ser la siguiente:

        before [;
         Take:
            if (correas hasnt general)
                "La funda está sujeta a la cama por unas correas.";
            if (funda has general)
                "La funda está atada a los barrotes.";
         Tie:
            if (second==0)
                "No tiene sentido hacer un nudo en la funda.";
            if (second==correas or catre or esqueleto or cuchillo)
                "No tiene sentido atar la funda ahí.";
            if (second==carbon or antorcha)
                "¿Estás loco?";
            if (second==barrote_flojo)
                "Mejor atarla a un barrote sólido.";
            if (second==barrotes or ventanuco) 
            {
                give funda general;
                move funda to location;
                "Atas la funda a uno de los barrotes más sólidos y 
                la dejas colgando por la ventana.";
            }
        ],

Fijate en un detalle. Cuando el jugador ATA con exito la funda a los barrotes, no sólo le damos el atributo general para marcar este hecho. Además, movemos la funda a la localización actual (que será la mazmorra, ya que de lo contrario los barrotes no estarían presentes y la acción no se habría generado). Esto lo hacemos para evitar que la funda siga en poder del jugador despues de haberla atado. Si no lo hicieramos, el jugador podría salir de la mazmorra y llevarse la funda consigo ¡mientras aún está atada a los barrotes!

Ya solo queda modificar la salida out_to de la mazmorra para que sólo deje salir al jugador cuando este haya resuelto el puzzle de los barrotes. Vamos allá:

        out_to [;
            if (barrote_flojo hasnt moved)
                "No puedes salir por el ventanuco, los barrotes no
                dejan suficiente hueco.";
            if (funda hasnt general)
                "No te atreves a saltar desde esta altura. Necesitas
                algún tipo de cuerda para descolgarte.";
            print "Contorsionándote, logras hacer pasar tu cuerpo por
                la estrecha abertura que dejó el barrote, y te
                descuelgas con cuidado usando la funda a modo de
                cuerda.^^";
            return ventanuco;

El final del juego

Bueno, si el jugador hace todo bien, conseguirá llegar a la localidad "Exterior de la torre", y se supone que ahí termina el juego. Pero si juegas lo anterior verás que el juego sigue esperando nuevos comandos del jugador, aunque realmente ya nada puede hacerse en esa localidad sin salidas.

La forma de dar por terminado el juego es hacer la asignación siguiente:

  deadflag=2;

Si al final del turno, Inform encuentra que deadflag vale 2, entiende que el juego ha terminado con éxito. Si vale 1, entiende que ha terminado con la muerte del aventurero. En ambos casos ya no le pide más órdenes, sino que le invita a rejugar la aventura o finalizar el programa, con un mensaje apropiado a cada caso.

El lugar obvio para hacer esa asignación es el momento en que el jugador sale por la ventana, esto es, en la rutina afuera de la mazmorra, cuando ha impreso el mensaje que explica cómo sale el jugador por la ventana, y va a retornar el objeto ventanuco.

La nueva rutina queda entonces así:

        out_to [;
            if (barrote_flojo hasnt moved)
                "No puedes salir por el ventanuco, los barrotes no
                dejan suficiente hueco.";
            if (funda hasnt general)
                "No te atreves a saltar desde esta altura. Necesitas
                algún tipo de cuerda para descolgarte.";
            print "Contorsionándote, logras hacer pasar tu cuerpo por
                la estrecha abertura que dejó el barrote, y te
                descuelgas con cuidado usando la funda a modo de
                cuerda.^^";
            deadflag=2;
            return ventanuco;

Como de costumbre, aquí tienes la versión completa del juego para que lo estudies, lo modifiques a tu antojo y lo pruebes.


Zak McKraken - spinf_2000@yahoo.com.
Adaptado a JIF por Luis Fernandez nfseoi (en) yahoo.es bajo la licencia Creative Commons Attribution-ShareAlike 2.0
Adaptado a JIF3+INFSP por Sarganar - sarganar (en) gmail.com bajo la misma licencia.

 

Parte III - Puliendo detallesParte II: Empieza lo interesanteRompecabezas un poco más complicadosLa ventana y los barrotes