|  |  | La 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:

- Una que indica a qué localidad conduce la puerta. Esta propiedad se llama `door_to`
- Otra que dice en qué lado de la habitación está la puerta, si al norte, al sur, etc.. Esta propiedad se llama `door_dir`

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á:

- El parser generará la acción `Go (Ir)` con `noun=out_obj`(`out_obj` es una forma que tiene Inform de representar la "dirección exterior", igual que tiene `n_obj` para representar la "dirección norte", etc).
- Para llevar a cabo esa acción, Inform mirará en primer lugar a dónde conduce la salida `out_to` de la mazmorra, y encontrará que conduce al ventanuco.
- Seguidamente verá que el ventanuco tiene el atributo `door`, de donde deducirá que no se trata del destino del viaje, sino de un mero tránsito. Ya que tiene el atributo `open`, el jugador puede pasar a través de ella.
- Entonces Inform buscará en la propiedad `door_to` del ventanuco a dónde debe ir a parar. Encontrará allí `ExteriorTorre` que ya es una localidad normal, de modo que moverá al jugador allí.

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.

| 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!

| 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.

|  |  | La ventana y los barrotes