¿Cómo hago para... incluir eventos ambientales aleatorios con Inform 7?
INTRODUCCIÓN
Este artículo surge como respuesta al primero de los artículos de 'cómo hago para...' sobre los que se ha estado hablando en este hilo del foro.
¿Qué es un efecto ambiental aleatorio? Es un mecanismo mediante el cual se intenta enriquecer la experiencia de una obra interactiva profundizando en la ambientación de alguna de las escenas mediante la inclusión de mensajes aleatorios de cosas que pasan, sin ser realmente cosas relevantes para la acción o hilo argumental principal. Estas cosas que pasan serían los eventos. Hay muchísimos ejemplos de esta técnica a lo largo de la historia de los relatos interactivos, uno de lo más recientes y cercanos es Modus Vivendi. En esta obra Incanus los usa para proporcionar una sensación más realista de una Roma repleta de vida.
¿Cuáles son las deseables características de los eventos ambientales?
- Deben ser aleatorios. Es la diversidad de su aparición lo que incrementa la credibilidad de la obra, la idea de que son cosas que 'pasan' más allá del control del usuario de la obra.
- No deben ser demasiado repetitivos. No pueden estar ocurriendo constantemente. Si algo como 'Las gaviotas cruzan el horizonte con un vuelo indolente', ocurre en cada turno, el escarnio público está asegurado. (Véase este caso, para darse cuenta de la importancia de limitar el uso de los eventos demasiado repetitivos). En nuestro mundillo hay un mal común que es la repetición en dos turnos consecutivos del mismo mensaje aleatorio, esto debe evitarse a toda costa.
- Sólo debe haber uno por turno. Los eventos aleatorios son decoración, ni siquiera debería uno cada turno, así que mucho menos dos en el mismo turno, ya que en lugar de enriquecer la ambientación provocarían el despiste del usuario.
- Deben ser coherentes. No hay nada peor que un efecto aleatorio que aparezca cuando no le corresponde (no sé digamos que el usuario acaba de lanzar un hechizo que ha congelado toda el entorno y entonces el sistema de eventos aleatorios le dice: "El sonido de las cigarras refuerza el calor de este verano.").
¿CÓMO HAGO... BÁSICO
En I7 la implementación de eventos aleatorios en su modo más básico no puede ser más sencilla, pero rvisemos primero las bases para lograrlos.
¿Cómo puedes hacer para ejecutar código cada turno? Mediante reglas 'Every turn', pero si vamos a usar esas reglas para eventos ambientales debe considerarse alguna reglas de coherencia esto se lograría añadiendo una condición 'when' en la regla, es decir:
Every turn when <condición de coherencia>:
<código a ejecutar>
Además queremos que los eventos aparezcan aleatoriamente, la forma más sencilla en I7 para lograr esto es mediante una condición 'a random chance of x in Y succeeds', de forma que el código anterior se transformaría en algo como:
Every turn when a random chance of <percent> in 100 succeeds and <condición de coherencia>:
<código a ejecutar>
¿Qué ponemos en el código a ejecutar? Lo más fácil es simplemente poner el 'evento' en si mediante un 'say'. Veamos un ejemplo en el que con una probabilidad del 15% un grillo canta en determinada localización, el código resultante sería:
Every turn when a random chance of 15 in 100 succeeds and the location is lugar nocturno:
say "El grillo canta una triste canción nocturna.".
Incluyendo tantas reglas como se deseen de este tipo se obtienen tantos eventos aleatorios, coherentes y adaptables como se quiera. Esta aproximación tiene al menos dos problemas:
- Parece que hay que describir demasiadas reglas para eventos diversos que afecten a las mismas condiciones de coherencia (por ejemplo, habría que escribir una regla como la anterior para cada uno de los eventos aleatorios que puedan ocurrir en la misma localidad).
- Nada impide que se repitan en dos turnos sucesivos el mismo evento, y lo que es peor, nada impide que haya varios en el mismo turno.
La solución a todos esos problemas viene de la capacidad de I7 de gestionar texto variable y aleatorio (véase sección 5.7 del manual). I7 nos proporciona una gran diversidad de posibles formas de gestionar textos aleatorios mediante los [one of] y los diversos elementos finales. Revisando la lista de posibles alternativas en la mencionada sección del manual vemos que 'at random' encaja perfectamente con lo que necesitamos ya que escoje uno de los textos aleatoriamente pero asegurándose de que no se repita el mismo mensaje dos veces. Supongamos que queremos que haya un efecto aleatorio cada tres turnos (aproximadamente) en 'lugar nocturno' el código sería:
Every turn when a random chance of 33 in 100 succeeds and the location is lugar nocturno:
say "[one of]Un grillo suena en la distancia.[or]Un olor a madreselva llega hasta tu nariz.[or]Una lejana y triste música parece provenir de la espesura.[at random]".
¿CÓMO HAGO... AVANZADO
¿Qué más podemos pedir a los eventos ambientales aleatorios? Pues muchas cosas como por ejemplo:
- Que cada evento aleatorio sólo se produzca una única vez hasta que ya no haya ninguno nuevo diferente por mostrar: esto se logra simplemente sustituyendo 'at random' por 'in random order'.
- Que cada evento tenga una probabilidad algo diferente: se logra usando 'as decreasingly likely outcomes' en lugar de 'at random'. (Y de esta forma hay muchos efectos que se pueden lograr mediante el uso del terminador adecuado, pero...)
- Que cada evento tenga exactamente una determinada probabilidad de aparecer: esto no lo tenemos resuelto con la aleatoriedad por defecto de textos de I7.
- Que los PNJs y otras entidades puedan reaccionar a los eventos.
- Que los eventos que hayan ocurrido puedan ser recordados para que cambie de alguna forma el devenir de la obra.
¿Cómo damos respuesta a todo esto?
Para empezar un pequeño truco que cada vez uso más. Lo que en el texto variable de I7 aparece entre corchetes (por ejemplo, "Aquí hay [number of apples in words] manzanas.") en realidad se limita a ejecutar una frase 'to say' (para el ejemplo sería algo como: To say (num - a number) in words: ... ) adecuada. Pero las frases 'to say' no tienen porqué limitarse a imprimir texto, pueden hacer muchas más cosas.
Pongamos un ejemplo, supongamos que en caso del código anterior los grillos o las madreselvas no le importan para nada a nuestra fiel compañera PNJ Peggy, pero que la música la entristece, podríamos usar el siguiente código para lograrlo:
To say music in the dark event:
if Peggy is in lugar oscuro:
increase peggy sadness by 1;
Last every turn when peggy sadness is greater than 10:
say "[one of]Peggy dice: 'Lo siento no puedo soportar más este lugar'[or]Peggy parece realmente triste.[stopping]".
Every turn when a random chance of 33 in 100 succeeds and the location is lugar nocturno:
say "[one of]Un grillo suena en la distancia.[or]Un olor a madreselva llega hasta tu nariz.[or]Una lejana y triste música parece provenir de la espesura.[music in the dark event][at random]".
Pero de nuevo parece que tendríamos que escribir una frase 'to say' para cada evento que requiera alguna clase de reacción y eso no parece muy eficiente, ¿cómo lo mejoramos?
Para empezar querríamos algo como 'to say (event - a random event)', ¿podemos? Si, sin duda, en I7 podemos definir clases nuevas de valores y hacerlo mediante tablas de esta forma:
Random event is a kind of value. The random events are defined by the Table of random events
Table of random events
random event
grillo suena
olor madreselva
lejana musica
Random event is a kind of value. The random events are defined by the Table of random events
Table of random events
random event description percent happened last
grillo suena "Un grillo suena en la distancia." 15 false 0
olor madreselva "Un olor a madreselva llega hasta tu nariz." 15 false 0
lejana musica "Una lejana y triste música parece provenir de la espesura." 30 false 0
random event | description | percent | happened | last |
grillo suena | Un grillo suena en la distancia. | 15 | false | 0 |
olor madreselva | Un olor a madreselva llega hasta tu nariz. | 15 | false | 0 |
lejana musica | Una lejana y triste música parece provenir de la espesura. | 30 | false | 0 |
En donde las columnas significarían:
- random event: el literal que codifica el evento.
- description: el texto que se imprimiría.
- percent: probabilidad en porcentaje de que ocurra el evento.
- happened: una marca para indicar que el evento ha sucedido.
- last: el turno en el que ocurrió por última vez.
Y ya que estamos, podríamos hacer que la 'fuente' de esos eventos no fuese necesariamente un lugar, sino también algún objeto o una región; para ello tendríamos que incluir declaraciones como la siguiente:
A thing has a list of random events called possible events. The possible events of a thing is usually {}.
A room has a list of random events called possible events. The possible events of a thing is usually {}.
A region has a list of random events called possible events. The possible events of a thing is usually {}.
[ Regla de evaluación de los eventos ]
Every turn:
[ Primero objetos presentes, luego location y finalmente la región ]
if no event of visible things happens and no event of the location happens and (location is not in a region (called container region) or no event of container region happens):
[ No events ]
do nothing.
[ Evalua un evento por separado ]
To decide if happens (event - a random event):
let prev be turn count;
decrease prev by 1;
if last of event is less than prev and a random chance of percent of event in 100 succeeds:
say description of event;
say paragraph break;
change last of event to turn count;
change happened of event to true;
decide yes;
otherwise:
decide no.
[ Verifica si se ejecuta algún evento dependiente algún objeto ]
To decide if any event of (V - value) happens:
sort possible events of V in random order;
repeat with event running through possible events of V:
if happens event:
decide yes;
decide no.
[ Versión negativa ]
To decide if no event of (V - value) happens:
if any event of V happens:
decide no;
otherwise:
decide yes.
[ Verifica sobre todos los objetos visibles ]
To decide if any event of visible things happens:
repeat with item running through visible things:
if any event of item happens:
decide yes;
decide no.
[ Versión negativa ]
To decide if no event of visible things happens:
if any event of visible things happens:
decide no;
otherwise:
decide yes.
Lugar nocturno is a room. "La noche. La espesura. La soledad."
The possible events of lugar nocturno are {grillo suena, olor madreselva, lejana musica}.
Completamente descriptivo. Podríamos ahora añadir un libro de reglas para reacciones de PNJs al estilo de I7. El resultado total final sería el siguiente código:
"Eventos aleatorios" Include Spanish by Sebastian Arg. Section 1 - Definitions Chapter 1 - Definition of random events Random event is a kind of value. The random events are defined by the Table of random events Table of random events random event description percent happened last grillo suena "Un grillo suena en la distancia." 15 false 0 olor madreselva "Un olor a madreselva llega hasta tu nariz." 15 false 0 lejana musica "Una lejana y triste música parece provenir de la espesura." 30 false 0 Chapter 2 - Extension of objects to have possible events A thing has a list of random events called possible events. The possible events of a thing is usually {}. A room has a list of random events called possible events. The possible events of a thing is usually {}. A region has a list of random events called possible events. The possible events of a thing is usually {}. Chapter 3 - Ver si se ejecutan los eventos The reacting actor is a thing that varies. To decide if happens (event - a random event): let prev be turn count; decrease prev by 1; if last of event is less than prev and a random chance of percent of event in 100 succeeds: say description of event; say paragraph break; change last of event to turn count; change happened of event to true; [ Hacer que los actores reaccionen ] repeat with actor running through visible person: change the reacting actor to actor; follow the random event reacting rulebook for event; decide yes; otherwise: decide no. To decide if any event of (V - value) happens: sort possible events of V in random order; repeat with event running through possible events of V: if happens event: decide yes; decide no. To decide if no event of (V - value) happens: if any event of V happens: decide no; otherwise: decide yes. To decide if any event of visible things happens: repeat with item running through visible things: if any event of item happens: decide yes; decide no. To decide if no event of visible things happens: if any event of visible things happens: decide no; otherwise: decide yes. Every turn: [ Primero objetos presentes, luego location y finalmente la región ] if no event of visible things happens and no event of the location happens and (location is not in a region (called container region) or no event of container region happens): [ No events ] do nothing. Chapter 4 - Reaccionando los PNJs a los eventos que pasan Random event reacting is a random event based rulebook. Random event reacting lejana musica when the reacting actor is Peggy: increase sadness of peggy by 1; if sadness of peggy is greater than 2: say "Peggy dice: 'oh... qué triste...'". Section 2 - Juego Lugar is a room. "La noche. La espesura. La soledad." The possible events of Lugar are {grillo suena, olor madreselva, lejana musica}. Peggy is a woman in Lugar. Peggy has a number called sadness. The sadness of peggy is 0.
¿CÓMO HAGO... CON LIBRERÍAS
Existe una librería inglesa que logra todos estos efectos mediante la escritura de tablas: Atmospheric Effects