Descripciones y parsingEl modelo del mundoPuntuaciónCómo extender o redefinir la librería

Cómo extender o redefinir la librería

La mayoría de los juegos necesitarán enriquecer el "modelo del mundo" que hemos estado viendo. En nuestro modelo hay conceptos como "recipientes", "vehículos", "objetos luminosos", etc. Pero seguramente tu juego necesitará otros conceptos que no están en la librería, como por ejemplo "amuletos mágicos". Quizás tu juego tenga una docena de objetos de este tipo, cada uno de ellos con unos efectos mágicos diferentes. Así que necesitarías rutinas para decidir cuándo un objeto es o no un amuleto, y qué hacer cuando el amuleto sea usado.

Para hacer esto, tu juego necesitará crear una "clase" para los amuletos. Llamemosla por ejemplo Amuleto. Entonces, la forma de comprobar si un objeto dado (por ejemplo, el indicado en la variable uno) es un amuleto sería:

 if (uno ofclass Amuleto) ...  

El hechizo concreto asociado con el amuleto (es decir, lo que ocurrirá al usar el amuleto) podría estar almacenado en la propiedad efecto_amuleto. Posibles valores de esta propiedad serían:

  efecto_amuleto "El amuleto se desintegra con un puf.",
  efecto_amuleto [;
        if (localizacion == laoscuridad)
        {
            give localizacion_real luz;
            "La estancia se ilumina con una luz sobrenatural."; 
        }
  ],
  efecto_amuleto  HabitacionOculta,
  efecto_amuleto [;
     return random(HabitacionDePlomo, HabitacionDePlata,
                   HabitacionDeOro);
  ],

El proceso de activar el amuleto X y desencadenar los efectos oportunos se reduce tan solo a enviarle el mensaje

  X.efecto_amuleto();

y esta activación puede retornar el valor false si no ha ocurrido nada, true si ha ocurrido algo, o bien retornar un objeto que sería la habitación a la cual debe ser teletransportado el jugador. A continuación un ejemplo de cómo se manejaría todo esto:

[ UsarAmuletoSub destino;
  if (uno ofclass Amuleto)
  {
    if (~~(uno provides efecto_amuleto))
       "[¡Hey! Olvidaste programar el efecto de este amuleto.]";
    destino=uno.efecto_amuleto();
    switch(destino)
    {
       false:   "No ocurre nada.";
       true:    ;  ! No hacemos nada especial
       default: print "Eres teletransportado mágicamente hasta...^";
                JugadorA(destino);
    }
  }
  else "Sólo puedes utilizar el verbo ~USA~ con un amuleto.";
];

Una extensión elaborada de la librería definirá muchas nuevas clases, gramáticas, acciones y definiciones de verbos. Todo esto podría ser empaquetado en un fichero de apellido .h y almacenarse con los demás ficheros de la librería, para ser incluido con Include en los juegos que lo necesiten.

(!)(!) Si dentro de ese fichero usas la directiva System_file; entonces sería posible incluso que los juegos que incluyan esta extensión puedan reemplazar (usando Replace) algunas de las rutinas proporcionadas en ella. Ver más adelante.

(!) Las propiedades definidas en la librería (como descripcion o al_e), se llaman "Propiedades comunes". Son especiales por la siguiente razón: si un objeto O no da ningún valor a la propiedad común P, la propiedad O.P puede ser examinada de todas formas sin que ocurran errores, si bien no se le puede dar ningún valor. Esto no podría hacerse con las propiedades inventadas para tu juego (como efecto_amuleto, es decir, si intentas ver lo que vale efecto_amuleto para un objeto en el cual no se haya escrito esta propiedad, el juego generará un error al ser jugado. En las propiedades comunes, todos los objetos tienen la propiedad definida a un valor por defecto (por ejemplo, la propiedad no_puedes_ir tiene como valor por defecto la cadena de texto "No puedes ir por ahí.", para todos los objetos que no den valor a esta propiedad.

Los valores por defecto de las propiedades pueden ser cambiados durante el juego, usando la rutina CambiarDefecto. Por ejemplo, en un estadio más avanzado del juego podrías poner:

CambiarDefecto(no_puedes_ir, "Eres un Master Aventurero, ¡y aún sigues 
               chocando contra las paredes!");

Por supuesto no puedes cambiar los valores por defecto de las propiedades "inventadas", ya que éstas no tienen valor por defecto.

(!)(!) Las propiedades comunes también son ligeramente más rápidas a la hora de hacer operaciones con ellas. El único inconveniente de las propiedades comunes es que su número está limitado. 63 en total, de las cuales la librería ya usa más de la mitad. Para indicar que una propiedad es una "propiedad común", debes usar la directiva Property. Por ejemplo:

  Property puerta_a;
  Property capacidad 100;
  Property no_puedes_ir "No puedes ir por ahí";    

En los dos últimos casos estás dando además su valor por defecto. En el primer caso el valor por defecto será 0.

Normalmente no se necesita hacer grandes cambios en la librería. Es mucho más frecuente que simplemente quieras cambiar los mensajes por defecto, como por ejemplo el mensaje "No hay nada en venta" que se genera para la acción Comprar, o el mensaje "Cogido." que aparece ante la acción Coger.

Esto es muy fácil de hacer, como se describe a continuación. Debes crear un objeto llamado MensajesLibreria, y su definición hay que ponerla entre la línea Include "EParser"; y la línea Include "Acciones";. Este objeto no necesita tener nombre ni descripción ni nada de esto. Tan sólo debe tener la propiedad antes y en ella se especifican los mensajes por defecto para cada acción. Por ejemplo:

Object MensajesLibreria
with   antes [;
          Saltar: "Saltas, y por unos instantes flotas inútilmente en
                   la gravedad cero de la Estación Espacial Alfa.";
          Encender:
               if (ml_n==3)
               {
                  print "Conectas la alimentación ", (del) ml_o, "."; 
               }
        ];

Este objeto nunca aparecerá físicamente en el juego, por supuesto. La idea es que su regla antes es consultada por la librería antes de imprimir ningún mensaje. Si el objeto MensajesLibreria retorna false, entonces la librería mostrará su mensaje estándar. Si retorna true, no imprimirá nada, suponiendo que el objeto ya ha impreso un mensaje apropiado.

Observando el ejemplo anterior, vemos que la acción Saltar simplemente imprime un mensaje, pero otras acciones (como Encender) necesitan imprimir un mensaje diferente según la situación particular (el caso extremo es la acción Coger, que puede generar hasta 13 mensajes diferentes, según el caso). La variable ml_n contiene un número que indica el mensaje concreto. Los posibles valores de ml_n y el mensaje estándar asociado a cada caso se muestra en un apéndice. Es posible que futuras ampliaciones de la librería añadan más números, pero se respetará la numeración de los que ya existían.

Un mensaje de la librería especialmente útil es el "prompt", cuyo valor habitual es "^>" (es decir, retorno de carro seguido de >). Ese texto es impreso ante la acción Prompt (es una acción ficticia que existe sólo con este propósito). De esta forma el prompt del juego puede hacerse sensible al contexto, o bien puede quitarse la línea en blanco que se genera tras cada turno.

(!) La acción Prompt se genera sólo durante el transcurso normal del juego, pero no en la lectura directa del teclado ni en preguntas de tipo si/no o en la elección REINICIAR/RESTAURAR/ABANDONAR.

(?) EJERCICIO 47  El juego "The Witness" de Infocom tenía un prompt que decía: "¿Qué harás ahora, detective?" en el primer turno, pero sólo "¿Ahora qué?" en los siguientes. Programa esto.
(Solución)

(!)(!) Una forma divertida de ver en acción el sistema de mensajes es escribir:

  Object MensajesLibreria
  with  antes [;
           print "[", sw__var, ", ", ml_n, "]";  
        ];   

en tu juego (nota ocultista: la variable sw__var, "variable switch", contiene en este caso el número de acción). Otro efecto divertido es simplemente retornar true, dando lugar a un juego alarmantemente silencioso.

(!)(!) Observar que MensajesLibreria puede usarse como una forma retorcida de añadir reglas extra al final del proceso de las acciones, ya que nada te impide hacer procesamiento real dentro de ella (esto es usado por ejemplo para implementar habitaciones en penumbra). Más comúnmente lo usarás para hacer que los mensajes del juego sean más sensibles al contexto, de forma que el mensaje "No hay nada en venta" puede convertirse en "Ese no es un artículo en venta", mientras el jugador esté dentro de una tienda.

(?) EJERCICIO 48  Escribe un juego en Inform en Catalán.
(Solución)

La propia librería está escrita en el lenguaje Inform, por lo que en teoría es posible modificarla. Y en la práctica no es tan complicado, con un poco de experiencia. Pero modificar los ficheros de la librería no es muy elegante y puede ser difícil después recordar dónde hicimos los cambios. Así que aquí tienes el último recurso: averigua qué rutina de la librería es la que te da los problemas (la que querrías cambiar) y reemplázala con otra usando la directiva Replace. Por ejemplo, si ponemos la directiva:

Replace QuemarSub;  

en tu fichero, antes de incluir los ficheros de la librería, entonces Inform ignorará la definición de QuemarSub cuando la encuentre en ficheros de librería (ficheros que lleven la directiva System_file). Así que podrás definir tu propia QuemarSub. Lo habitual será que copies la definición que estaba en la librería (en este caso estaría en el fichero accionm.h, que es donde están todas las rutinas de manejo de acciones), la "pegues" en tu juego, y hagas las modificaciones deseadas sobre la versión "pegada".

La rutina más frecuentemente reemplazada es DibujarLineaEstado. En la sección sección sobre ensamblador tienes varios ejemplos.

(!)(!) Inform te deja incluso reemplazar mediante Replace algunas funciones "hardware", como random, que normalmente serían convertidas directamente en código Z. Obviamente reemplazar algo como child por una rutina software causaría una ralentización notable de la ejecución, y un ligero aumento en el tamaño del código. Reemplazar random, sin embargo, puede ser útil para fijar el generador de números aleatorios, de cara a probar el juego.


Zak McKraken - spinf@geocities.com

Descripciones y parsingEl modelo del mundoPuntuaciónCómo extender o redefinir la librería