|  |  | Programando la antorcha (¡por fin!)

## Programando la antorcha (¡por fin!)

### Cambiar las respuestas

Necesitamos un modo de discernir qué acción es la que está intentando el jugador, para que la respuesta de la antorcha no sea siempre la misma. Esto es muy sencillo, cambiemos la antorcha por esto:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take: print "La antorcha está incrustada en la pared.^";
                    rtrue;
                  ],
    has female;

Ahora, el `print` y el `rtrue` sólo se ejecutarán si la acción era `Take` (¡cuidado! Hablamos de acciones, no de verbos, recuerda). Cualquier otra acción es ignorada por la antorcha, y por tanto causará la acción por defecto.

Si queremos que la antorcha reaccione de la misma forma ante dos o más acciones diferentes, basta poner éstas separadas por comas, por ejemplo:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn: print "La antorcha está incrustada en la pared.^";
                    rtrue;
                  ],
    has female;

Ahora el `print` y el `rtrue` se ejecutarán para las acciones `Take` y `Turn`, las restantes acciones seguirán siendo tratadas por la librería.

Si queremos diferentes mensajes de respuesta ante diferentes acciones, basta ir repitiendo una estructura similar a la anterior, por ejemplo ¿qué pasa si el jugador pone SOPLA LA ANTORCHA? Intentémoslo (con el listado de acciones activado para ver qué acción se genera):

    >sopla antorcha
    [ Action Blow with noun 31 (antorcha) ]
    Tu soplido no produce ningún efecto.

¡Vaya! Inform tenía prevista una acción `Blow (Soplar)`, y la respuesta por defecto no parece del todo mala, pero cambiémosla por otra más apropiada:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn: print "La antorcha está incrustada en la pared.^";
                    rtrue;
             Blow: print "Iluso, el fuego que arde en esta antorcha no
                     es de este mundo.^";
                    rtrue;
                  ],
    has female;

¡Acuerdate de poner el `rtrue`! (¿Qué pasaría si no lo pones?) La verdad es que repetir `rtrue` después de cada `print` es un poco pesado. Por eso Inform nos proporciona una abreviatura: el comando `print_ret` imprime una cadena e inmediatamente retorna con `rtrue`. Además, imprime una "linea nueva" al final del texto, por lo que nos evita tener que poner el `^` en cada mensaje. Usando esta abreviatura, la cosa quedaría así:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn: print_ret "La antorcha está incrustada en la pared.";
             Blow: print_ret "Iluso, el fuego que arde en esta antorcha no
                     es de este mundo.";
                  ],
    has female;

Aún es posible abreviar más. ¿No te suena esto de "imprimir, añadir salto de linea y retornar inmediatamente"? Todo ello podía abreviarse sin más que poner el mensaje entre comillas. Así:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn: print_ret "La antorcha está incrustada en la pared.";
             Blow: "Iluso, el fuego que arde en esta antorcha no
                     es de este mundo.";
                  ],
    has female;

Esta forma es muy habitual, porque resulta muy cómoda. Sin embargo es un poco "rara" para el principiante, ya que en realidad se trata de una orden `print`, pero el `print` no se escribe. Además retorna inmediatamente (con `rtrue`), y eso tampoco se ve claramente en el código. Con todo, si sabes esto, no deberías tener problemas con su uso.

Como ves, añadir mensajes específicos para la antorcha resulta muy sencillo. ¡Añade tú mismo alguno! Por ejemplo, ¿tendrá Inform previsto HUELE ANTORCHA? ¿COME ANTORCHA? ¿QUEMA ANTORCHA? ¿TOCA ANTORCHA? Pruebalo, verás que todos ellos están previstos, pero el mensaje de respuesta estándar puede no encajar muy bien. Averigua qué acciones genera cada una de las frases anteriores y modifica la antorcha para que imprima mensajes más apropiados.

### Haciendo que aparezca la puerta

Pero ahora pasemos a algo más serio. Hasta aquí, nos hemos limitado a variar los textos de respuesta de la antorcha, pero no hemos programado nada "realmente", en el sentido de que todas estas acciones no tienen efecto sobre el juego. Queremos que, al empujar la antorcha, aparezca una salida en `EscaleraCaracol2`. Recuerda que las salidas de una localidad son propiedades de esa localidad. Ahora tenemos un pequeño problema:

| Una vez que el juego ha comenzado, no se pueden añadir o quitar propiedades a los objetos, pero sí que es posible cambiar el valor de cualquier propiedad de cualquier objeto.

Por tanto no podemos hacer que `EscaleraCaracol2` empieze sin su propiedad `w_to`, y más adelante en el juego de pronto adquiera esta propiedad. Sin embargo, lo que si podemos hacer es que `EscaleraCaracol2`, inicialmente tenga la propiedad `w_to`, pero esta propiedad tome el valor cero (que es lo mismo que decir que esa salida no lleva a ningún lugar). Más adelante, cuando se empuje la antorcha, cambiamos el valor de esta propiedad para que sea igual a `Mazmorra`.

Es decir, edita el juego y añade de nuevo la salida `w_to` en la escalera, pero en lugar de hacer que lleve a `Mazmorra`, haz que "lleve a 0", así:

    Object  EscaleraCaracol2 "Escalera de caracol"
     with   description "Los desgastados peldaños de piedra resbalan en
                ocasiones. A mitad de la escalera una antorcha en la
                pared impide que la oscuridad sea completa.",
            d_to EscaleraCaracol1,
            w_to 0,
            u_to AltoTorre,
     has    light;

Cuando una salida "lleva a 0 (cero)", el jugador no podrá pasar por ella. Es como si no existiera. Pero el declararla de este modo nos permitirá cambiar su valor más adelante en el juego, mediante una instrucción como esta:

     EscaleraCaracol2.w_to = Mazmorra;

Que puedes leer así: "Cambia la propiedad `w_to` del objeto `EscaleraCaracol2`, y haz que sea igual a `Mazmorra`". Si quisieramos eliminar esa salida, basta hacerla igual a cero de nuevo:

     EscaleraCaracol2.w_to = 0;

¿Cuándo debe ejecutarse este código? Evidentemente cuando el jugador empuje o tire de la antorcha. Cuando empuja la conexión se crea, cuando tira, la conexión desaparece. Es decir:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn:  "La antorcha está incrustada en la pared.";
             Blow:  "Iluso, el fuego que arde en esta antorcha no
                     es de este mundo.";
             Push: EscaleraCaracol2.w_to = Mazmorra;
                      "Al empujar la antorcha una porción de pared se abre
                       al oeste dando acceso a una estancia.";
             Pull: EscaleraCaracol2.w_to = 0;
                    "Al tirar la antorcha, la puerta secreta se cierra
                     de nuevo.";
                  ],
    has female;

¿Por qué he puesto la asignación antes del mensaje? ¿Daría igual hacerlo al revés?

Intenta probar si la cosa funciona. Empuja la antorcha, se abre la conexión, tira de ella, se cierra. ¡Bien! Empuja de nuevo, se abre otra vez. Y ahora que está abierta ¡empuja otra vez! ¿qué ocurre? Hum, esto no queda muy bien, el mensaje diciendo que la pared se mueve aparece de nuevo.

Deberíamos comprobar si la conexión existe antes de mostrar ese mensaje. Un poco más de programación:

    Object antorcha "antorcha" EscaleraCaracol2
    with   name 'antorcha',
           before [;
             Take, Turn:  "La antorcha está incrustada en la pared.";
             Blow:  "Iluso, el fuego que arde en esta antorcha no
                     es de este mundo.";
             Push: if (EscaleraCaracol2.w_to==0) {
                         EscaleraCaracol2.w_to = Mazmorra;
                         "Al empujar la antorcha una porción de pared se
                         abre al oeste dando acceso a una estancia.";
                     }
                     else "La antorcha no cede más.";
             Pull: if (EscaleraCaracol2.w_to == Mazmorra) {
                        EscaleraCaracol2.w_to = 0;
                        "Al tirar la antorcha, la puerta secreta se cierra
                        de nuevo.";
                     }
                     else "La antorcha no cede más.";
                  ],
    has female;

Hemos usado aqui una estructura condicional, es decir, una forma de decirle a Inform que cierto código sólo debe ejecutarse si se cumple cierta condición. La estructura `if...else` tiene la siguiente sintaxis:

      if (condicion) {
        ! Secuencia de instrucciones que deben ejecutarse
        ! si la condición se cumple
      }
      else {
        ! Secuencia de instrucciones que deben ejecutarse
        ! si la condición no se cumple
      }

Observaciones:

- La secuencia de instrucciones a la que se hace referencia en esta sintaxis utiliza punto y coma (`;`) como delimitador de instrucciones.
- Si esta secuencia de instrucciones tiene una sola instrucción, entonces podemos no poner llaves `{`, `}` a su alrededor. Sin embargo, si tiene más de una instrucción es obligatorio poner esas llaves.
- La parte `else` es opcional. Si no la ponemos, entonces no se ejecutará nada en caso de que la condición no se cumpla.

| ¡Cuidado! Una pifia típica es olvidar las llaves cuando hay más de una instrucción. Considera el ejemplo siguiente:
      if (talisman in player)
         AumentarFuerza();
         print "Sientes como tu fuerza aumenta.";
Parece que el programador pretendía aquí que la fuerza del jugador aumente si lleva un talismán consigo (la condición `talisman in player` sería cierta en este caso), y además mostrar un mensaje indicandole ésto al jugador. Sin embargo, ha olvidado poner llaves alrededor de estas instrucciones. ¿Qué crees que pasará? ¿Dará un mensaje de error Inform? La respuesta es que no habrá mensaje de error, pero en cambio el juego no funcionará de la forma prevista. Seguramente lo verás más claro si elimino un par de espacios delante de `print` (para Inform es lo mismo que haya dos espacios o que haya doce):
      if (talisman in player)
         AumentarFuerza();
      print "Sientes como tu fuerza aumenta.";
¿Vés ahora mejor lo que ocurre? Al no poner llaves, Inform entiende que la "secuencia de instrucciones a ejecutar en caso de que la condición sea cierta", se compone de una sola instrucción: la llamada a la rutina `AumentarFuerza()`. El `print` que le sigue se considera que ya está "fuera del `if`", y por tanto no depende de la condición. Es decir, el mensaje "Sientes como tu fuerza aumenta" se imprimiría incluso si el jugador no lleva talismán (¡sin embargo su fuerza no aumentaría!).

Una vez explicado `if`, podemos ver con mayor claridad qué ocurre cuando el jugador empuja la antorcha. El código que se ejecutará entonces será el siguiente (un extracto de la rutina `before` de la antorcha):

    Push: if (EscaleraCaracol2.w_to==0) {
                EscaleraCaracol2.w_to = Mazmorra;
                "Al empujar la antorcha una porción de pared se
                abre al oeste dando acceso a una estancia.";
            }
            else "La antorcha no cede más."

La condición que comprobamos es `EscaleraCaracol2.w_to==0`. El doble signo `==` significa que se intenta comprobar la igualdad, y no debe ser confundido con un solo `=` que sería una asignación como ya hemos visto. Por tanto la condición evaluada es "¿el valor de la propiedad `w_to` del objeto `EscaleraCaracol2` es igual a cero?" Si esta condición es cierta, se ejecutará lo que viene entre llaves a continuación. Si es falsa se ejecutará lo que va tras el `else`. Date cuenta de que si es cierta, esto implica que hacia el oeste la salida aún no existe, y por tanto lo que hacemos es crearla y emitir el mensaje. Y si es falsa, es que la salida hacia el oeste ya había sido creada, y por tanto emitimos un mensaje indicando que no ocurre nada más.

El código para el caso de la acción `Pull (Tirar)` es totalmente análogo.

### Condiciones

Inform puede comprobar muchos tipos de condiciones diferentes. Ya hemos visto uno de ellos: la igualdad. Las comparaciones numéricas tienen una sintaxis bastante lógica: `>` significa "mayor que", `<` es "menor que", `>=` significa "mayor o igual" y `<=` sería "menor o igual".

Además de estos casos sencillos, hay otras comprobaciones interesantes que necesitarás usar para programar juegos:

`~=`
  Desigualdad. El sigo `~` que aparece delante del igual se obtiene en la mayoría de los ordenadores pulsando AltGr y el 4. Si estás acostumbrado a programar en C, ten cuidado de no confundirte y usar `!=` ya que en Inform el carácter `!` es el comentario como ya hemos dicho.
`in`
  Estar dentro. El resultado de esta condición es `true` (verdad) si el objeto que nombras a la izquierda del `in` está dentro del que nombras a su derecha. Por ejemplo `antorcha in EscaleraCaracol2` es cierto, y sin embargo `antorcha in player` es falso (¡a menos que el jugador haya cogido la antorcha de alguna forma!)

  Ten en cuenta que en Inform los objetos pueden estar dentro de otros y estos a su vez dentro de otros. Si el jugador llevara consigo una caja dentro de la cual hay una moneda, sería cierto `caja in player` y `moneda in caja`, pero no sería cierto `moneda in player` (la condición `in` se limita a comprobar la inclusión en un solo nivel).

`has`
  Tener un atributo. El resultado de esta condición es `cierto` si el objeto que nombras a la izquierda de `has` tiene activado el atributo que nombras a su derecha. Por ejemplo `antorcha has female` sería cierto (puesto que en la declaración del objeto `antorcha` le hemos puesto el atributo `female`), o `EscaleraCaracol2 has light` también sería cierto (pues esta localidad tiene el atributo `light`). En cambio, `antorcha has luz` es falso pues no le hemos dado este atributo a la antorcha (pensandolo bien, parece que deberíamos haberselo dado, ¿no? De todas formas en este juego es intrascendente porque la antorcha nunca se mueve de su localidad)

  Observa que `has` se limita a evaluar si el objeto dado tiene ese atributo, pero no tiene inteligencia alguna para hacer deducciones. Por ejemplo, si llevas una linterna que tiene el atributo `light` y con ella entras en una cueva que no tiene el atributo `light`, será cierto `linterna has light` pero falso que `cueva has light`. Es decir, `has` no es capaz de deducir que hay luz en la cueva (suministrada por la linterna). Para comprobar si hay luz en una localidad deberías comprobar si la localidad "`has light`" o si alguno de los objetos que tiene dentro "`has light`". Por suerte no tienes que molestarte en comprobar esto. La librería lo hace automáticamente en cada turno (de hecho hace algo mucho más complejo, pues tiene en cuenta si los objetos están dentro de otros. Así, si llevas la linterna dentro de una caja opaca, la librería deduciría que no hay luz visible, pero si la llevas dentro de una caja transparente sí la habría).

`hasnt`
  Lo contrario de `has`. La condición `objeto hasnt atributo` es cierta si el objeto tiene ese atributo desactivado. Por tanto, `Cueva hasnt light` será cierto si la cueva no tiene luz.

Hay más condiciones, pero estas son las más importantes y las más usadas. Puedes combinar varias condiciones en una sola. Por ejemplo, si quieres que algo se ejecute sólo si el jugador lleva un talismán, y además el talismán tiene `light`, puedes combinar ambas condiciones usando el operador `&&`, en la forma siguiente:

      if ((talisman in player) && (talisman has light)) ...

Observa cómo hay que encerrar cada condición entre paréntesis, y colocar el operador `&&` entre ambas.

Si lo que quieres es ejecutar algo cuando es cierta una condición cualquiera entre varias, por ejemplo, si el jugador lleva el talismán o si lleva el anillo, entonces el operador es `||` (con algunos teclados el símbolo `|` se obtiene pulsando AltGr y 1):

      if ((talisman in player) || (anillo in player)) ...

Puedes poner tantas condiciones como quieras combinadas con estos operadores, e incluso mezclar operadores. Piensa por ejemplo cómo harías para escribir una condición que sea "si el jugador (player) lleva el talismán con luz, o bien si lleva el anillo puesto" (para verificar si lleva el anillo puesto debes mirar si `anillo has worn`, además de comprobar que está en el jugador).

Inform tiene también una abreviatura muy cómoda para el caso de que estés comprobando varias igualdades alternativas. Por ejemplo, imagina que quieres emitir un mensaje si el jugador está en la localidad llamada `Mazmorra`, o en la localidad `Pasadizo` o en la localidad `Sotano`. En principio puede hacerse usando el operador `||` para diferentes comparaciones con la variable `localizacion`, así:

      if ((location==Mazmorra) || (location==Pasadizo)
          || (location==Sotano))  print "¡Qué frio!^"

Pero también es posible usar la siguiente forma abreviada:

      if (location== Mazmorra or Pasadizo or Sotano)
         print "¡Qué frio!^";

Esta abreviatura (`or`) sólo puede aplicarse para comparaciones de igualdad, en las que el elemento a la izquierda de la comparación es siempre el mismo, y queremos compararlo contra diferentes valores.

|  |  | Programando la antorcha (¡por fin!)