CREANDO UNA AVENTURA CONVERSACIONAL CON Z88DK (II)
Seguimos en esta ocasión por donde lo dejamos en la entrega anterior. Recordemos que el
objetivo de esta serie de artículos es practicar con las funciones de texto de la
librería z88dk, por medio de un ejemplo de programa que, aun siendo posible que se
desarrolle con otras herramientas, como BASIC, parsers, etc., es bastante ilustrativo, y
además nos permite estar haciendo un juego desde el primer momento.
En la entrega anterior habíamos comenzado a escribir las aventuras de Guybrush
Threpwood, un aspirante a pirata, que debía dirigirse a un maestro en la taberna de la
isla de Melêe e impresionarle con algún truco. Habíamos dado los pasos precisos para que
nuestro personaje fuera capaz de desplazarse entre las distintas habitaciones de la
taberna, con las funciones de texto del z88dk, que habíamos visto que eran muy parecidas
a las del C estándar de PC. También vimos como aplicar unos efectos interesantes al
texto (negrita, cursiva, subrayado y texto inverso).
En el presente artículo veremos alguna técnica para incorporar objetos a nuestro mundo,
incluyendo la posibilidad de interactuar con ellos, y la creación de un inventario.
También veremos como aplicar efectos de color al texto (hasta ahora, todo era en un
aburrido blanco y negro). Sin más dilación comenzamos.
¿Qué necesitamos?
Si seguimos la entrega anterior no tenemos que realizar ninguna preparación adicional.
Todo el código que vayamos añadiendo se incluirá en los archivos aventura.c y datos.h.
Creando los objetos
Lo primero de todo es crear la estructura de datos que nos permitirá que los objetos
puedan ser manejados durante el juego, igual que se hizo en el caso de las habitaciones.
Esta estructura de datos para los objetos contendrá una serie de valores que serán de
utilidad, como el nombre del objeto, dónde se encuentra, cuánto pesa, etc.
En concreto, podemos añadir el siguiente código a datos.h (da igual si es antes o
después de la definición de THabitacion):
typedef struct
{
char nombre[35];
int localizacion;
int peso;
} TObjeto;
|
Todos los objetos de nuestro juego serán variables del tipo TObjeto. El primer campo
(nombre) contendrá el nombre con el que aparecerá el objeto en la pantalla, como por
ejemplo: "una antorcha", "un anillo", "la espada", etc. El campo de localizacion
indicará en que posición del mapeado de juego se encuentra el objeto, aunque también
puede indicar diferentes estados del objeto (como por ejemplo, que el objeto está en
nuetros inventario, que está destruido, que todavía no se ha creado, que lo llevamos
puesto, etc.). Por último, el peso, como es evidente, indica cuánto pesa el objeto. Es
corriente en las aventuras que el protagonista sólo pueda llevar un número limitado de
cosas, y el campo peso permite controlar esto.
Hablemos del campo localización. Hemos dicho que ese campo nos va a indicar en qué lugar
se encuentra el objeto. Si recordamos, cada habitación de nuestra aventura tenía un
identificador, que coincidía con su posición en el array de habitaciones (menos uno). Si
queremos que el objeto esté en una de las habitaciones, el campo localización deberá
contener el identificador de esa habitación (o la posición en el array de habitaciones).
Por ejemplo, supongamos que queremos crear un objeto, una espada, que se encuentre en la
habitación número 5 (la cocina). Lo que debemos hacer es almacenar en el campo
localización de la variable que represente a la espada el valor 5 (más adelante veremos
ejemplos).
El campo de localización es un campo dinámico; es decir, puede cambiar de valor durante
el transcurso de la aventura. Por ejemplo, si la espada deja de estar en la cocina para
pasar a estar en el salón (porque el personaje la haya cogido de la cocina y la haya
dejado en el salón), el valor de localización pasará a ser 4.
Por lo tanto, el campo localización indica la habitación en la que se encuetra el
objeto. Sin embargo, existe una serie de localizaciones especiales, que no se
corresponden con habitaciones de nuestra aventura. Uno de estos valores es el -1. Si el
campo localización de un objeto vale -1, significa que el objeto está en posesión del
jugador (y que por lo tanto, al consultar el inventario o listado de objetos que porta,
se le indicará que lo lleva). Aparte podemos inventarnos todas las localizaciones
especiales que quisieramos, por ejemplo, la 254 para los objetos que tenemos puestos, la
255 para los que están destruidos, etc. (más adelante hablaremos también de ello).
Antes de seguir programando, hemos de pensar: ¿qué objetos va a tener nuestra aventura?
En nuestro caso concreto vamos a tener tres:
- Una espada, que encontraremos en la cocina de la taberna.
- Una jarra, que portará el jugador consigo desde el comienzo de la aventura.
- Una antorcha, que podremos recoger en el callejón.
De momento vamos a incluir la espada y la jarra en nuestra aventura. La antorcha es un
tipo de objeto más complicado que trataremos más adelante. En el archivo aventura.c, en
el método main, justo después de la inicialización de las habitaciones, podemos incluir
el siguiente código (el código en rojo es el que se añade):
THabitacion habitaciones[6];
TObjeto objetos[4];
inicializarHabitaciones(habitaciones);
inicializarObjetos(objetos);
|
Como en el caso de las habitaciones, creamos un array que contendrá todos los objetos de
nuestra aventura. En nuestro caso, este array tendrá tamaño 4, para la espada, la jarra,
y dos para la antorcha (no os impacientéis, luego entenderéis por qué se usan dos
objetos para la antorcha). Evidentemente, si ahora compilamos no va a funcionar, porque
falta por crear la función inicializarObjetos que, como en el caso de
inicializarHabitaciones, se encarga de rellenar el array de objetos. Nuestra función
inicializarObjetos podría tener la siguiente forma (se debe recordar que tiene que estar
antes de la función main):
void inicializarObjetos(TObjeto objetos[])
{
strcpy(objetos[0].nombre,"una espada");
objetos[0].localizacion = 5;
objetos[0].peso = 5;
strcpy(objetos[1].nombre,"una jarra");
objetos[1].localizacion = -1;
objetos[1].peso = 3;
}
|
(la antorcha todavía no la hemos creado... sí, somos pesados con la antorcha). El primer
objeto es la espada, y al principio de la aventura se encontrará en la habitación cuyo
identificador sea el 5 (la cocina); decimos al principio porque, como hemos comentado
antes, eso puede cambiar. La espada tiene un peso de 5. El siguiente objeto es la jarra,
cuya localización inicial es la -1, que hemos dicho que era la localización especial que
usábamos para indicar que el objeto estaba en posesión del protagonista de la aventura.
El último paso es que, junto a la descripción de las habitaciones, se nos indiquen los
objetos que podemos encontrar en ellas. Eso, evidentemente, es necesario hacerlo en la
función escribirDescripcion, y se hará de forma similar a como se escribían las posibles
direcciones; lo único es que tendremos que recorrer el array de objetos comprobando
cuáles están en la habitación. Por lo tanto, tenemos que pasar como parámetro el array
de objetos. La función podría quedar de la siguiente forma (el código en rojo es nuevo):
void escribirDescripcion(THabitacion habitaciones, int habitacion, TObjeto objetos)
{
int hayObjetos = 0;
int i;
printf(habitaciones[habitacion].descripcion);
printf("\n\n");
printf("Salidas:");
if (habitaciones[habitacion].direcciones[0] != 0)
printf(" %c[4mNorte%c[24m",27,27);
if (habitaciones[habitacion].direcciones[1] != 0)
printf(" %c[4mEste%c[24m",27,27);
if (habitaciones[habitacion].direcciones[2] != 0)
printf(" %c[4mSur%c[24m",27,27);
if (habitaciones[habitacion].direcciones[3] != 0)
printf(" %c[4mOeste%c[24m",27,27);
printf("\n\n");
printf("En la habitacion puedes ver:");
for (i=0;i<4;i++)
if (objetos[i].localizacion == habitaciones[habitacion].id)
{
printf(" %s",objetos[i].nombre);
hayObjetos = 1;
}
if (hayObjetos == 0)
printf(" nada");
printf("\n\n");
}
|
Como hemos añadido un parámetro de entrada más, en todas las líneas a las que se llame a
la función se debe añadir también ese parámetro. Estas líneas (en la función main)
quedarían de la siguiente forma:
escribirDescripcion(habitaciones,habitacion,objetos)
|
El código que hemos añadido recorre el array de objetos comprobando para cada uno si su
localización corresponde con el identificador de la habitación actual. En el caso de que
sea así escribe su nombre (fíjate en el espacio en blanco antes del %s). La variable
hayObjetos nos sirve para determinar si hay algún objeto en la habitación. En un
principio vale 0, y si la localización de algún objeto se corresponde con el
identificador de la habitación actual, valdrá 1. Solo en el caso de que valga 0 se
mostrará el mensaje "nada". Ahora ya podemos compilar y ejecutar la aventura,
dirigiéndonos hacia la cocina para comprobar que la espada está allí. No podemos saber
nada de la jarra, porque todavía no hemos implementado el inventario, lo haremos en la
siguiente sección.
|
¿Quién se habrá dejado esta espada aquí tirada en
la cocina?
|
Coger y dejar objetos. El inventario
A partir de este momento, el secreto para poder realizar acciones con los objetos
durante la aventura consiste nada más que en añadir posibles frases a usar por el
jugador en el intérprete de comandos. Si recordamos, el intérprete de comandos era la
parte del código que leía la cadena de texto introducida por el jugador y comparaba con
el vocabulario conocido con el programa. Para incluir los comandos de inventario y coger
y dejar objetos debemos añadir código a esta parte.
Implementemos primero el inventario. Cuando el jugador teclee "inventario" o "i", se
deberán mostrar por pantalla todos los objetos que porta. Podemos añadir el siguiente
código al intérprete de comandos, dentro del método main (el código en rojo es el que se
ha añadido):
}
else
printf("\n\nNo puedo ir en esa direccion\n\n");
}
else if (strcmp(comando,"i") == 0 || strcmp(comando,"inventario") == 0)
{
hayObjetos = 0;
printf("\n\nLlevas:");
for (i = 0; i<4;i++)
if (objetos[i].localizacion == -1)
{
printf(" %s",objetos[i].nombre);
hayObjetos = 1;
}
if (hayObjetos == 0)
printf(" nada");
printf("\n\n");
}
else
printf("\n\nNo entiendo lo que dices\n\n");
}
|
La variable hayObjetos se debe definir al comienzo de la función main y cumple el mismo
objetivo que en la función describirHabitacion. Lo único que se hace en el caso del
inventario es recorrer el array de objetos escribiendo el nombre de aquéllos para los
que el valor de localización sea -1, o "nada" en el caso de que no haya ninguno que
cumpla esta condición. Si compilamos y ejecutamos, por fin veremos la jarra en nuestro
juego al teclear "i" o "inventario".
|
Esperemos que esta jarra nos sirva de algo en
nuestra aventura
|
Para que el jugador pueda coger y dejar objetos, lo único que tenemos que hacer,
nuevamente, es añadir los comandos adecuados al intérprete de comandos. La
implementación es tan simple como hacer que cuando cojamos un objeto, la localización
del mismo pase a ser -1, y que cuando dejemos un objeto, la localización del mismo pase
de ser -1 a ser el identificador de la habitación actual. Veamos primero cómo
implementar la parte de coger objetos (el código en rojo es código añadido):
else if (strcmp(comando,"i") == 0 || strcmp(comando,"inventario") == 0)
{
hayObjetos = 0;
printf("\n\nLlevas:");
for (i = 0; i<4;i++)
if (objetos[i].localizacion == -1)
{
printf(" %s",objetos[i].nombre);
hayObjetos = 1;
}
if (hayObjetos == 0)
printf(" nada");
printf("\n\n");
}
else
// Comandos con más de una palabra
{
strcpy(palabra,strtok(comando," "));
if (strcmp(comando,"coger") == 0)
{
strcpy(palabra,strtok(0,"\0"));
if (palabra == 0)
printf("\n\nNecesito que me digas que tengo que coger\n\n");
else
{
hayObjetos = 0;
i = 0;
while (hayObjetos == 0 && i<4)
{
if (strcmp(objetos[i].nombre,palabra) == 0 &&
objetos[i].localizacion == habitacion+1)
{
objetos[i].localizacion = -1;
hayObjetos = 1;
printf("\n\nHe cogido %s\n\n",palabra);
}
i++;
}
if (hayObjetos == 0)
printf("\n\nNo puedo hacer eso\n\n");
}
}
else
printf("\n\nNo entiendo lo que dices\n\n");
}
|
Para que este código funcione, se debe crear una nueva variable (al inicio de la función
main):
¿Qué significa todo lo que hemos añadido? En primer lugar, se ha realizado una
modificación importante al intérprete de comandos. hasta ahora, solo era capaz de
interpretar comandos de una única palabra. Ahora le hemos añadido la posibildad de
comprender comandos de más de una palabra. Simplemente, primero comprobamos si el
comando introducido por el jugador se corresponde con alguno de los de una palabra. y al
final introducimos el código que interpreta más de una. Este código empieza con la
instrucción strcpy(palabra,strtok(comando," ")).
La función strtok es otra de las que nos ofrece z88dk y su uso es exactamente igual al
del estándar en el PC; strtok(cadena1,cadena2) devuelve la primera subcadena de cadena1,
estando todas las subcadenas de cadena1 delimitadas por el carácter especificado en
cadena2. Así pues, si cadena2 vale " " (espacio en blanco) como en el código anterior,
la llamada a esta función devolverá los primeros carácteres de cadena1 hasta llegar al
primer espacio en blanco (o el final de la cadena), es decir, la primera palabra. A
partir de este momento, si vamos llamando a strtok pasando el valor 0 como cadena1, se
nos irán devolviendo subcadenas sucesivas a partir de la cadena inicial. Hay que
destacar que esta cadena inicial queda modificada; además, el código anterior, al
compilarlo, nos mostrará una serie de warnings, que pueden ser ignorados (el programa
funciona correctamente). Como lo que se devuelve es una cadena, es necesario utilizar
strcpy para almacenar el valor devuelto en una variable de tipo char[].
Si la primera palabra es "coger", entramos en la parte del código que interpreta este
comando. Se utiliza de nuevo strtok para almacenar en palabra el resto del comando (pues
se indica como delimitador el símbolo de final de cadena \0). Si el comando solo se
compone de la palabra coger se mostrará un mensaje de error adecuado. Lo siguiente es
inicializar las variables i, que se utilizará como contador para recorrer el array de
objetos, y hayObjetos, que en este caso se utilizará para saber si se ha encontrado un
objeto con el mismo nombre que el tecleado por el jugador. A continuación, se recorre el
array de objetos buscando un objeto cuyo nombre se corresponda con el introducido por el
jugador. El principal inconveniente es que el jugador tiene que introducir el nombre
completo, incluyendo el artículo, en el caso de que se hubiera puesto. Por ejemplo, si
el nombre del objeto es "una espada", el jugador deberá teclear "coger una espada" para
poder obtenerla. Se deja como ejercicio al lector arreglar este pequeño fallo.
Al recorrer el array no sólo se comprueba si existe algún objeto cuyo nombre se
corresponda con el introducido, sino que también el objeto debe encontrarse en la misma
habitación que el jugador. Coger el objeto consiste nada más en cambiar el valor del
campo localización del objeto a -1.
Obsérvese que, tanto si el objeto existe como si no, tanto si el objeto está en la misma
habitación como si no, se muestra el mismo mensaje de error ("No puedo hacer eso"). Esto
es básico para no darle pistas al jugador sobre los objetos que existen en nuestro
juego.
El código para dejar objetos es prácticamente igual; la única diferencia es que
comprobamos que para dejar un objeto éste se encuentre en el inventario (localizacion =
-1) y que dejar un objeto significa cambiar el valor del campo localizacion al del
identificador de la habitación actual (el código en rojo es el que se ha añadido):
if (hayObjetos == 0)
printf("\n\nNo puedo hacer eso\n\n");
}
}
else if (strcmp(comando,"dejar") == 0)
{
strcpy(palabra,strtok(0,"\0"));
if (palabra == 0)
printf("\n\nNecesito que me digas que tengo que dejar\n\n");
else
{
hayObjetos = 0;
i = 0;
while (hayObjetos == 0 && i<4)
{
if (strcmp(objetos[i].nombre,palabra) == 0
&& objetos[i].localizacion == -1)
{
objetos[i].localizacion = habitacion+1;
hayObjetos = 1;
printf("\n\nHe dejado %s\n\n",palabra);
}
i++;
}
if (hayObjetos == 0)
printf("\n\nNo puedo hacer eso\n\n");
}
}
else
printf("\n\nNo entiendo lo que dices\n\n");
}
char palabra[50];
|
Un último detalle que nos queda por comentar es el del peso de los objetos. Recordemos
que uno de los campos de TObjeto era el peso. Supongamos que el peso máximo que puede
llevar un jugador es de 6. Lo que tenemos que añadir para poder controlar el peso en
nuestro juego es lo siguiente:
- Crear una variable que almacene el peso de los objetos portados por el jugador.
- Controlar que el peso total al coger un objeto no supere el peso máximo que puede
llevar un jugador. En caso de no ser así sumar el peso del objeto recogido al peso
total.
- Al dejar un objeto, restar su peso al peso total.
Para resolver el primer punto, creamos la variable pesoTransportado, al inicio de la
función main:
Y la inicializamos con el peso del objeto que porta el jugador nada más empezar. Esto lo
podemos hacer justo después de inicializar los objetos:
inicializarHabitaciones(habitaciones);
inicializarObjetos(objetos);
pesoTransportado = objetos[1].peso;
|
Para resolver el segundo punto, añadimos el siguiente código a la hora de manejar que el
jugador coja objetos (código en rojo):
Y para el tercer punto introducimos el siguiente código (la línea en rojo):
if (strcmp(comando,"coger") == 0)
{
strcpy(palabra,strtok(0,"\0"));
if (palabra == 0)
printf("\n\nNecesito que me digas que tengo que coger\n\n");
else
{
hayObjetos = 0;
i = 0;
while (hayObjetos == 0 && i<4)
{
if (strcmp(objetos[i].nombre,palabra) == 0
&& objetos[i].localizacion == habitacion+1)
{
hayObjetos = 1;
if (objetos[i].peso + pesoTransportado <= 6)
{
objetos[i].localizacion = -1;
printf("\n\nHe cogido %s\n\n",palabra);
pesoTransportado += objetos[i].peso;
}
else
printf("\n\nNo puedo transportar mas peso\n\n");
i++;
}
if (hayObjetos == 0)
printf("\n\nNo puedo hacer eso\n\n");
}
}
|
Objetos que cambian de estado
Hasta ahora muy pocas características de z88dk nuevas hemos introducido. Aprovechamos
este apartado para indicar cómo añadir colores a nuestras aventuras conversacionales
gracias a esta librería. Para ello introducimos un nuevo objeto, la antorcha, que es
especial, porque vamos a poder encenderla y apagarla. Antes habíamos comentado que este
tipo de objetos que se encienden y se apagan tienen que ser creados como dos objetos
distintos. En el caso de la antorcha, podemos introducir el siguiente código en la
función inicializarObjetos:
c[0] = 27;
c[1] = '\0';
strcpy(objetos[2].nombre,"una antorcha ");
strcat(objetos[2].nombre,c);
strcat(objetos[2].nombre,"[44mapagada");
strcat(objetos[2].nombre,c);
strcat(objetos[2].nombre,"[47m");
objetos[2].localizacion = 2;
objetos[2].peso = 2;
strcpy(objetos[3].nombre,"una antorcha ");
strcat(objetos[3].nombre,c);
strcat(objetos[3].nombre,"[43mencendida");
strcat(objetos[3].nombre,c);
strcat(objetos[3].nombre,"[47m");
objetos[3].localizacion = -2;
objetos[3].peso = 2;
|
Para que funcione tenemos que declarar la variable c al principio de la función como:
Se han creado dos objetos. El objeto de índice 2 se corresponde con la antorcha apagada
y el objeto de índice 3 con la antorcha encendida. La antorcha apagada en un principio
se encuentra en la localización con identificador 2 (el callejón) y tiene peso 2. La
antorcha encendida, evidentemente, tiene el mismo peso, y al campo localizacion se le ha
asignado un valor -2; vamos a usar el valor -2 en la localización para designar a
aquellos objetos que no existen en el juego por alguna razón (por ejemplo, porque se
hayan destruido, porque se tengan que contruir, objetos que representan diversos estados
de un objeto, como por ejemplo una antorcha encendida apagada o encendida o un baúl
abierto o cerrado, etc.). Esto quiere decir que si nos desplazamos a la localización 2,
encontraremos la antorcha apagada, pero será imposible encontrar la antorcha encendida
en ninguna localización de la aventura, y tampoco en nuestro inventario.
Es en el nombre de los objetos donde encontramos la dificultad. Si recordamos en la
entrega anterior, cuando escribíamos texto con formato (subrayado, en negrita, etc.), se
usaba printf de la siguiente manera:
printf("%c[4mTexto subrayado%c[24m",27,27);
|
Es decir, escribimos el carácter número 27, el símbolo [, y 4m indicando, por ejemplo,
formato subrayado (o 24m, indicando texto sin subrayar). Como el carácter 27 es no
imprimible, recurríamos a la facilidad que nos presentaba printf de escribir caracteres
mediante %c. Para crear una cadena con color, el procedimiento es el mismo; sin embargo,
la función strcpy no permite usar %c, así que lo que hacemos es ir concatenando,
mediante el uso de strcat (que funciona igual que en el C del PC). Por ejemplo, en el
caso de la antorcha apagada, deseamos que aparezca por pantalla la palabra 'apagada' con
color de fondo azul. Copiamos en el nombre del objeto la cadena "una antorcha ", le
concatenamos el carácter 27 (utilizando el pequeño truco visto en el código anterior),
concatenamos el formato ([44m se corresponde con color de fondo azul), concatenamos la
palabra "apagada", volvemos a concatenar el carácter 27 y el formato ([47m hace que el
fondo vuelva a ser blanco); por lo tanto, entre ambos formatos especificados, el fondo
aparecerá de color azul. Lo mismo se ha realizado con la antorcha encendida, pero usando
el color amarillo.
Por lo tanto, si en una cadena escribimos el carácter 27, el símbolo [, un código de
color xx, y la m, a partir de ese momento, si xx se encuentra entre los valores de la
siguiente lista, el texto tendrá el color de fondo correspondiente al texto:
40 - negro
41 - rojo
42 - verde
43 - amarillo
44 - azul
45 - magenta
46 - cyan
47 - blanco
Para el color del texto propiamente dicho, empleamos la misma táctica, pero utilizando
los siguientes códigos de formato:
30 - negro
31 - rojo
32 - verde
33 - amarillo
34 - azul
35 - magenta
36 - cyan
37 - blanco
Si compilamos y cargamos el juego en el emulador, veremos que si nada más empezar nos
desplazamos hacia el este, la antorcha estará allí, pero no la podremos coger. Esto se
debe a que para coger un objeto teníamos que introducir el nombre exacto del mismo, y
eso es imposible, porque el nombre exacto de la antorcha apagada es: "una antorcha
{27}[44mapagada{27}[47m", que contiene un carácter que no se puede escribir (el 27).
Esto lo vamos a tener que solucionar añadiendo dos nuevos comandos al intérprete de
comandos, "coger antorcha" y "dejar antorcha". El código se muestra a continuación:
else if (strcmp(comando,"i") == 0 || strcmp(comando,"inventario") == 0)
{
hayObjetos = 0;
printf("\n\nLlevas:");
for (i = 0; i<4;i++)
if (objetos[i].localizacion == -1)
{
printf(" %s",objetos[i].nombre);
hayObjetos = 1;
}
if (hayObjetos == 0)
printf(" nada");
printf("\n\n");
}
else if (strcmp(comando,"coger una antorcha") == 0)
{
if (objetos[2].localizacion == habitacion + 1)
{
if (objetos[2].peso + pesoTransportado <= 6)
{
objetos[2].localizacion = -1;
printf("\n\nHe cogido %s\n\n",objetos[2].nombre);
pesoTransportado += objetos[2].peso;
}
else
printf("\n\nNo puedo transportar mas peso\n\n");
}
else if (objetos[3].localizacion == habitacion + 1)
{
if (objetos[3].peso + pesoTransportado <= 6)
{
objetos[3].localizacion = -1;
printf("\n\nHe cogido %s\n\n",objetos[3].nombre);
pesoTransportado += objetos[3].peso;
}
else
printf("\n\nNo puedo transportar mas peso\n\n");
}
else
printf("\n\nNo puedo hacer eso\n\n");
}
else if (strcmp(comando,"dejar una antorcha") == 0)
{
if (objetos[2].localizacion == -1)
{
objetos[2].localizacion = habitacion + 1;
pesoTransportado -= objetos[2].peso;
printf("\n\nHe dejado %s\n\n",objetos[2].nombre);
}
else if (objetos[3].localizacion == -1)
{
objetos[3].localizacion = habitacion + 1;
pesoTransportado -= objetos[3].peso;
printf("\n\nHe dejado %s\n\n",objetos[3].nombre);
}
else
printf("\n\nNo puedo hacer eso\n\n");
}
else
// Comandos con más de una palabra
{
|
Lo que se ha hecho es aplicar el código general de coger y dejar objetos a la antorcha
de tal forma que tan sólo sea necesario escribir "una antorcha" a la hora de coger o
dejar el objeto, esté encendida o apagada.
Por útlimo introducimos dos comandos más, "encender antorcha" y "apagar antorcha". Para
encender la antorcha es necesario que la antorcha apagada (objeto de índice 2) se
encuentre en nuestro inventario (el valor de su campo localizacion debe ser -1). Lo que
se hará será cambiar su valor de localizacion a -2, para que el objeto desaparezca del
juego, y cambiar el de la antorcha encendida (objeto de índice 3) a -1, para que
aparezca en nuestro inventario. Para apagar la antorcha se sigue el proceso contrario.
Se observa que al cambiar de estado la antorcha lo único que pasa es que un objeto
desaparece del juego y otro es introducido. El código se muestra a continuación:
}
else if (strcmp(comando,"encender antorcha") == 0)
{
if (objetos[2].localizacion == -1)
{
objetos[2].localizacion = -2;
objetos[3].localizacion = -1;
printf("\n\nHe encendido la antorcha\n\n");
}
else
printf("\n\nNo puedo hacer eso\n\n");
}
else if (strcmp(comando,"apagar antorcha") == 0)
{
if (objetos[3].localizacion == -1)
{
objetos[3].localizacion = -2;
objetos[2].localizacion = -1;
printf("\n\nHe apagado la antorcha\n\n");
}
else
printf("\n\nNo puedo hacer eso\n\n");
}
else
// Comandos con más de una palabra
{
|
|
La antorcha en acción |
Resumen
¿Qué es lo que hemos visto en esta entrega? Resumimos:
- Cómo añadir objetos a nuestro juego, y permitir manejarlos (coger, dejar,
inventario, etc.).
- Cómo añadir efectos de color a nuestro juego de texto (lo hemos visto de forma muy
limitada, ya practicaremos más en posteriores entregas).
- El uso de strtok y strcat.
Está claro que si queremos permitir que el jugador realice más acciones lo único que
tenemos que hacer es introducir nuevos comandos en el intérprete de comandos tal cómo se
ha visto hasta ahora.
Hemos hablado de objetos normales y objetos que se pueden encender/apagar, pero en una
aventura podemos encontrar otro tipo de objetos, como objetos que se pueden llevar
puestos (una chaqueta, un sombrero), objetos que pueden contener a otros (un baúl, una
petaca), etc. Para crear este tipo de objetos podemos jugar, como en el caso de la
antorcha, con valores especiales del campo localizacion y con varios objetos para
distintos estados de un mismo objeto (antorcha encendida/apagada, baúl abierto/cerrado,
etc.). Se deja como ejercicio al lector implementar este tipo de objetos
Una curiosidad - violación de segmento
Por último, una curiosidad que el autor de este texto ha podido comprobar mientras
realizaba este tutorial; el tema de las violaciones de segmento, con respecto a las
cadenas. Por lo que se ha visto hasta ahora, no parece producirse una violación de
segmento en ejecución si hay algún problema de memoria con punteros que se nos escape;
simplemente se mostrarán caracteres extraños por pantalla... además, ¿cómo sería una
violación de segmento en un Spectrum? ¿Similar a un RANDOMIZE USR 0?