ContenidosMúsica y sonidoOtros trucos

Otros trucos

Entrada por ratón

Las ventanas de tipo texto y las de tipo rejilla tienen la interesante capacidad de reconocer cuándo se pulsa un ratón sobre ellas, y de retornar las coordenadas de la letra o pixel que se ha seleccionado. Para sacar partido de esta capacidad, primero debes alertar al programa de que tiene que estar alerta para detectar las pulsaciones de ratón en la ventana o ventanas que te interesen. Por ejemplo, digamos que tienes una ventana gráfica, gg_ventana_brujula, en la que has puesto una imagen de una rosa de los vientos de ocho puntas. Quieres que el jugador pueda clicar sobre cualquiera de las puntas y que eso se convierta en un comando para ir en la dirección apropiada. Lo primero, necesitas añadir la siguiente línea a la rutina Inicializar():

   glk_request_mouse_event(gg_ventana_brujula);

El juego estará alerta para detectar una pulsación, y sólo una, del ratón en la ventana que tiene la rosa de los vientos. Si no ocurre ninguna, no pasa nada; el programa aceptará igualmente la entrada habitual por el teclado. Pero si ocurre una, se disparará la rutina HandleGlkEvent(). HandleGlkEvent() ya ha aparecido en secciones previas (mira "Imágenes en una ventana gráfica", por ejemplo, o en "Música y sonido") pero este es un caso especialmente claro de cómo funciona la rutina. Como siempre, la rutina HandleGlkEvent() recibe los parámetros ev y contexto; esta vez ev, que es un array, traerá bastante información. Cuando el programa detecta una pulsación de ratón, rellena el array con la siguiente información:

Tu rutina HandleGlkEvent, entonces, tendría el siguiente aspecto (al menos en parte):

   [ HandleGlkEvent ev contexto;
      switch (ev-->0) {
         evtype_MouseInput:
            glk_request_mouse_event(gg_ventana_brujula);
            ! Aqui pondriamos codigo para analizar los resultados y
            ! generar instrucciones nuevas. Por ejemplo, podriamos
            ! hacer que si ev-->2 esta entre 50 y 100, mientras
            ! ev-->3 esta entre 0 y 50, entonces se habria pinchado
            ! en la flecha norte de la brujula, y entonces
            ! lanzariamos el comando para ir al norte
      }
   ];

Obsérvese que, una vez que el juego ha detectado una pulsación del ratón, dejará de detectar más pulsaciones de ratón en esa ventana, a menos que le indiquemos que espere por otra. De ahí la llamada a a glk_request_mouse_event() que aparece en el código anterior. Ahora, debido a que el juego está esperando a que el jugador complete su línea de comandos, no podemos simplemente lanzar un comando interno en el juego del tipo <<Ir Obj_n>>. En vez de eso, tendremos que hacer lo siguiente: Primero, vete hasta la línea en que comienza la rutina HandleGlkEvent y declara tres nuevas variables locales: abortres, comandonuevo y longcomando. Después, tras aceptar el click de ratón, cancelaremos la entrada de línea del teclado, poniendo:

   glk_cancel_line_event(gg_mainwin, 0);

Ahora, imagina un comando que, tecleado por el jugador, haría lo que quieres que ocurra. En este ejemplo, el comando podría ser "ir norte", o "norte". Haz que éste sea el nuevo comando en la forma siguiente:

   nuevocomando = "ir norte";

Y después copia el siguiente código tal cual:

   longcomando = PrintAnyToArray(abortres+WORDSIZE,
      INPUT_BUFFER_LEN-WORDSIZE, comandonuevo);
   abortres-->0 = longcomando;

Lo que hace lo anterior es completar el proceso para decirle al computador que en este caso particular, pinchar el ratón en ese área concreta de la ventana gráfica, equivale exactamente a teclear "ir norte" y pulsar Intro. Ahora, puedes hacer que ese texto aparezca tras el prompt, para que el jugador vea lo que ha ocurrido, o simplemente hacer algo como:

   glk_set_style(style_Input);
   print "(click de ratón)";
   glk_set_style(style_Normal);
   new_line;

Qué método es el mejor depende de los gustos personales, y del caso particular que tengamos entre manos. En cualquier caso, la última línea de estos bloques de código ha de ser return 2;, para indicar que este turno ha finalizado. Et voilà - ya has manejado la entrada por ratón.

Hiperenlaces

Hiperenlaces son imágenes o trozos de texto que, cuando el usuario pincha con el ratón sobre ellos, enviarán un mensaje a HandleGlkEvent(), indicando que han sido pinchados, lo que te permite ejecutar código especial si quieres. Por ejemplo, podrías insertar notas al pie "clickables", que muestren su texto si el jugado pincha en ellas. O permitir a los jugadores que se muevan simplemente pinchando sobre los nombres de las salidas en las descripciones de las localidades... hay infinitas posibilidades. Los hiperenlaces se preparan de forma muy similar a los eventos de ratón para ventanas gráficas o de rejilla. Lo primero que hay que hacer es alertar al programa de que debe estar atento a las pulsaciones de ratón sobre un hiperenlace:

   glk_request_hyperlink_event(gg_mainwin);

(Cambia el nombre de la ventana en la que aparecerán los hiperenlaces, si no es gg_mainwin. Si quieres enlaces en muchas ventanas, tendrás que repetir una línea como la anterior para cada una de ellas.) Ahora el programa está listo para responder a un enlace, sólo tienes que construir los enlaces propiamente dichos. Esto se hace con la llamada a glk_set_hyperlink(). Esta función recibe un parámetro, que debe ser un número distinto de cero que identifique de forma única a tu enlace. Probablemente querrás crear constantes para referirte a los números que hayas elegido para los enlaces. En tu código, algo como ENLACE_IR_NORTE es bastante más claro que algo como, por ejemplo, 67. Cualquier texto o imagen que se muestre en pantalla después de la llamada a glk_set_hyperlink() será parte del enlace, hasta que ejecutes una llamada a glk_set_hyperlink(0). Si dos enlaces van seguidos, puedes omitir esta llamada entre ambos. Tan pronto como comienza un enlace nuevo, se da por terminado el anterior. Ahora tus enlaces ya están listos, y todo lo que necesitas es programar HandleGlkEvent() para que responda a ellos cuando el jugador pinche en uno. Por ejemplo, podría ser así:

   [ HandleGlkEvent ev contexto abortres comandonuevo longcomando;
      switch (ev-->0) {
         evtype_Hyperlink:
            glk_request_hyperlink_event(gg_mainwin);
            ! Pon codigo aqui analogo al que vimos para manejar
            ! el raton. La unica diferencia es que ahora ev-->1
            ! contiene la ventana, y ev-->2 el numero del link 
            ! sobre el que el jugador ha pinchado
      }
   ];

Y al igual que en la sección previa, recuerda que tras responder a un click sobre un hiperenlace, el programa no responderá a otro, a menos que explícitamente le digas que lo haga.

Pausas y tiempo real

Aunque las aventuras, generalmente, están basadas en turnos, es posible -y en realidad bastante sencillo, una vez que te has familiarizado con la entrada por ratón y por hiperenlaces- incorprar eventos de tiempo real en tu juego. Esto puede estar bien para, por ejemplo, mover a los personajes, que entren y salgan de la localidad mientras el jugador está pensando qué comando dar después, o para implementar una bomba que el jugador realmente deba desactivar en unos segundos. También pueden usarse en conjunción con gráficos para crear animaciones. El comando para lanzar un evento de tiempo real es glk_request_timer_events(), con un número entre los paréntesis, que representa el número de milisegundos que transcurrirán entre los sucesivos eventos de tiempo real. Es decir, cuando el programa llega a una línea que dice glk_request_timer_events(1000), se ejecutará el código apropiado de HandleGlkEvent() cada 1000 milisegundos, o sea, cada segundo. Detectar este tipo de evento es muy similar a los del ratón o los hiperenlaces, símplemente equipa a tu rutina HandleGlkEvent() con algo como lo siguiente:

   [ HandleGlkEvent ev contexto;
      switch (ev-->0) {
         evtype_Timer:
            ! Aqui pones el codigo a ejecutar cada cierto tiempo
      }
   ];

La principal diferencia entre estos eventos y los de raton o de hiperenlace, es que no necesitas solicitar de nuevo un evento de tiempo real cada vez que has consumido uno: el programa seguirá generandolos automáticamente hasta que le digas explícitamente que ya no genere más, con la línea glk_request_timer_events(0); Así que, ¿cómo harías una animación? La clave es sacar partido del hecho de que, si enumeras cada cuadro de la animación de forma correlativa en tu fichero de recursos Blorb, las constantes que se les asignarán serán números secuenciales. Así que si tienes una animación con siete cuadros, cada cuadro podría recibir los números 3, 4, 5, 6, 7, 8 y 9, por ejemplo. Pero no necesitas saber qué números son, basta que crees un contador global para los cuadros y le asignes como valor inicial el del primer cuadro:

   cuenta_cuadros = Primer_Cuadro;

Después, pon un código como el siguiente en la rutina HandleGlkEvent():

   switch (ev-->0) {
      evtype_Timer:
         if (cuenta_cuadros > Ultimo_Cuadro) glk_request_timer_events(0);
         else {
            glk_image_draw(gg_ventana_anim, cuenta_cuadros, 0, 0);
            cuenta_cuadros++;
         }
   }

Este código mostrará primero el primer cuadro, después el segundo, después el tercero, etc. hasta llegar al séptimo, y después parará. Es cosa tuya asegurarte de que esto ocurra a una velocidad razonable - intenta diferente número de milisegundos desde tu glk_request_timer_events() y mira cuál queda mejor. Ahora, si lo único que quieres es una pausa hasta que el jugador pulse una tecla, la cosa es mucho más sencilla. Hay un comando llamado KeyCharPrimitive() que espera por una tecla. Si pones en tu código la línea x = KeyCharPrimitive(); el juego esperará a que el jugador pulse una tecla, y después pondrá el valor de la tecla pulsada en la variable x. Un interesante efecto secundario es que puedes usar KeyCharPrimitive() a solas para causar una pausa en la salida, y esperar hasta que el jugador pulse una tecla antes de continuar. Por ejemplo, supón que quieres hacer una pausa antes de una revelación dramática:

   print "~He considerado todas las pruebas~, dice el detective,
          ~y todo apunta en una dirección. El asesino fue...";
   KeyCharPrimitive();
   print " ¡yo! Lo siento, no volverá a ocurrir.~";

Entrada/Salida a fichero

La mayoría de los sistemas de programación de aventuras tienen previsto algún tipo de entrada/salida a fichero, aunque sólo sea para crear y leer partidas guardadas en disco. Inform Glulx permite al programador crear y leer ficheros con cualquier tipo de contenido. Por ejemplo, puedes escribir una serie de juegos con el mismo protagonista, y transferir información sobre el personaje de un juego al siguiente. Esto puede hacerse si el primer juego crea un fichero que contiene la información pertinente, y el segundo lo lee. He aquí cómo. Para crear un fichero, lo primero de todo, debes crear un objeto conocido como fileref, de modo que el programa tenga una forma de referirse al fichero. Esto se puede hacer de dos formas. Si quieres que el nombre del fichero sea fijo, siempre el mismo, elegido por el programa, el comando es glk_fileref_create_by_name; si prefieres que sea el jugador el que elija el nombre del fichero, usarás glk_fileref_create_by_prompt. Ambos necesitan tres parámetros. El primer parámetro indica qué tipo de fichero queremos crear. Consiste en dos constantes, unidas por un signo más. La primera constante será fileusage_Data (otras posibilidades se usan sólo para ficheros con partidas grabadas y similares). La segunda constante será o bien fileusage_BinaryMode o fileusage_TextMode; el modo Binary se usa para ficheros que sólo serán leídos desde otros juegos Glulx (como en el ejemplo de antes que almacenaba datos del personaje), mientras que el modo Text es para ficheros que puedan ser leidos por el jugador (como una lista de cosas divertidas para probar en el juego, un certificado de haber finalizado, o lo que se te ocurra). El tercer parámetro (veremos el segundo en un momento) es la ROCA del fichero que vamos a crear. Las rocas para filerefs son análogas a las de las ventanas o los canales de sonido. Por supuesto, si estás creando un fichero simplemente para escribir información en él y cerrarlo a continuación, no necesitarás una roca, ya que las rocas son para seguir la pista a los objetos Glk entre diferentes sesiones de juego, y no va a ocurrir un cambio de sesión a mitad de la escritura del fichero. Así que en este caso puedes poner un 0 para este parámetro. Si necesitas mantener el fichero abierto durante varios turnos, en cambio, necesitarás crear una roca para él. El segundo parámetro depende del método de creación del fichero. Si lo has creado en la forma en la que el programador especifica el nombre del fichero, aquí sería donde iría ese nombre. Sin embargo, hay dos complicaciones. En primer lugar, querrás que el nombre de fichero funcione en diferentes ordenadores y plataformas, así que lo más fiable es que te limites a nombres con ocho caracteres o menos (ya que algunas plataformas no pueden manejar nombres más largos). En segundo lugar, no puedes simplemente poner el nombre del fichero en este punto, sino que tienes que envolverlo en una llamada especial: ConvertAnyToCString(). Por ejemplo, para crear un fichero llamado INFO en el que poder escribir después, habría que poner:

   fref = glk_fileref_create_by_name(fileusage_Data+fileusage_BinaryMode,
      ConvertAnyToCString("INFO"), 0);

Si vas a dejar que el jugador elija el nombre del fichero, el segundo parámetro contiene información sobre el tipo de fichero que se va a crear. Ya que se usa el mismo comando para crear ficheros de lectura o de escritura, aqui hay cuatro opciones:

En este caso podríamos terminar con una llamada como:

   fref = glk_fileref_create_by_prompt(fileusage_Data+fileusage_BinaryMode,
      filemode_Write, 0);

Una vez que has obtenido el fileref, puedes abrir el fichero:

   str = glk_stream_open_file(fref, filemode_Write, 0);

El segundo parámetro es uno de los filemodes que hemos listado antes. Si has pedido el nombre del fichero al jugador, debe ser el mismo filemode que usaste antes. Una vez que el fichero ha sido abierto, podemos deshacernos del fileref:

  glk_fileref_destroy(fref);

Para escribir, hacemos que el stream actual sea ese fichero, y después imprimimos texto normalmente (como si fuera para la pantalla):

   glk_stream_set_current(str);
   print "PUNTOS DE EXPERIENCIA: ", jugador.exp, "^";

Una vez finalizado, volvemos a la ventana principal, y cerramos el stream:

   glk_set_window(gg_mainwin);
   glk_stream_close(str, 0);

El segundo parámetro de glk_stream_close(), al igual que en glk_window_close(), sólo tiene interés si quieres conocer cuántos caracteres han sido escritos o leídos. En cualquier otro caso, usa cero. Para leer de un fichero, el proceso es muy similar, solo que usando filemode_Read. Las funciones para leer los datos son glk_get_char_stream() para leer un único carácter, glk_get_buffer_stream() para leer un array de bytes, y glk_get_line_stream() para leer una línea de bytes, hasta el siguiente carácter de línea nueva. Mira el paquete Ork para más detalles. (Ork 1 genera un fichero con información sobre el jugador; Ork 2 permite al jugador cargar el personaje que quieran, o empezar de cero.)


ContenidosMúsica y sonidoOtros trucos