#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <list>
using namespace std;
/*Para colocar el cursor en la posición que queramos de la consola. Para ello utilizamos una clase definida
en la biblioteca windows.h la HANDLE y la COORD y creamos dos objetos. También hacemos uso de de
la función SetConsoleCursorPosition()*/
void gotoxy(int x, int y){
HANDLE hCon; //indentificador de la consola
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
COORD dwPos;
dwPos.X = x;
dwPos.Y = y; //Las coordenadas "Y" están invertidas
SetConsoleCursorPosition(hCon, dwPos);
}
/*Para evitar que el cursor esté parpadeando en pantalla. Utilizamos
una entrada semejante a la de la función gotoxy, y utilizamos la
función SetConsoleCursorInfo que sirve para mofificar las caracteristicas
del cursor (hacerlo más pequeño, grande, quitarlo,etc). Debemos crear
un objeto de la clase CONSOLE_CURSOR_INFO que debemos mandar como parametro
de la función, y modificaremos sus datos miembros que son dos: dwSize para
controlar el tamaño y bVisible para que se muestre el cursor o no. Al pasar
el objeto a la función SetConsoleCursorInfo hay qeu hacerlo usando un "&".*/
void OcultarCursor(){
HANDLE hCon; //indentificador de la consola
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
cci.dwSize = 2;
cci.bVisible = FALSE;
SetConsoleCursorInfo(hCon, &cci);
}
/*Esta clase servirá para crear e interactuar con nuestro objeto nave.*/
class Nave{
private:
int x, y;
const char ARRIBA;
const char ABAJO;
const char IZQUIERDA;
const char DERECHA;
int corazones;
int vidas;
public:
/*Constructor para asignar una posición inicial*/
Nave(int coordenada_x, int coordenada_Y, int _corazones, int _vidas, char sube, char baja, char giro_derecha, char giro_izquierda)
:ARRIBA(sube), ABAJO(baja), IZQUIERDA(giro_izquierda), DERECHA(giro_derecha)
{
x = coordenada_x;
y = coordenada_Y;
corazones = _corazones;
vidas = _vidas;
}
/*Este método sirve como su nombre indica para pintar la nave en pantalla,
para ello hemos usado caracteres ASCII.*/
void Pintar(){
gotoxy(x,y); printf(" %c",30);
gotoxy(x,y+1); printf(" %c%c%c",40,207,41);
gotoxy(x,y+2); printf("%c%c %c%c",30,190,190,30);
}
/*Este método borra el rastro de la posición anterior la nave*/
void BorrarNave(){
gotoxy (x,y); printf(" ");
gotoxy (x,y+1); printf(" ");
gotoxy (x,y+2); printf(" ");
}
/*Este método sirve para borrar un rastro más grande como el que deja la explosión*/
void BorrarExplosion(){
gotoxy (x,y); printf(" ");
gotoxy (x,y+1); printf(" ");
gotoxy (x,y+2); printf(" ");
}
/*Este método sirve para mover la nave mediante las teclas.
Para detectar cuando pulsamos alguna tecla, y hacer algo en
de que tecla pulsemos. Usamos la función kbhit() incluida en
la biblioteca conio.h. También es necesario usar la función
getch() para recuperar la tecla que pulsemos y asignarla a
una variable. Al principio debemos imprimir un espacio en blanco
para borrar la posición anterior.*/
void Mover(){
if(kbhit()){
char tecla = getch();
BorrarNave();
if (tecla == IZQUIERDA && x>3)
x--;
else if(tecla == DERECHA && x<73)
x++;
else if(tecla == ARRIBA && y>3)
y--;
else if(tecla == ABAJO && y<25)
y++;
Pintar();
}
}
/*Este método sirve para marcar los límites y que la nave no pueda sobrepasar
los bordes establecidos y dibujados*/
void PintarLimites(){
int ancho;
int alto;
for (ancho = 2; ancho < 79; ancho++){
gotoxy(ancho, 28); printf("%c",205);
gotoxy(ancho, 2); printf("%c",205);
}
for (alto=2; alto < 29; alto++){
gotoxy(2, alto); printf("%c",186);
gotoxy(78, alto); printf("%c",186);
}
gotoxy(2,2); printf("%c",201);
gotoxy(78,2); printf("%c",187);
gotoxy(2,28); printf("%c",200);
gotoxy(78,28); printf("%c",188);
}
/*Este método sirve para dibujar los corazones que simbolizarán la vida de
nuestra nave*/
void PintarVidaSalud(){
gotoxy(50,1); printf("Vidas:%d",vidas);
gotoxy(64,1); printf("Salud:");
gotoxy(70,1); printf(" ");
for(int i=0; i<corazones; i++){
gotoxy(70+i,1); printf("%c",3);
}
}
/*Este método es el encargado de quitar los corazones cuando los asteroides
colisionen con la nave*/
void QuitaCorazones(){
corazones--;
}
/*Este método sirve para pintar una recreación de una explosión para
simular la muerte de la nave*/
void Morir(){
if(corazones==0){
BorrarExplosion();
gotoxy(x,y); printf(" ** ");
gotoxy(x,y+1); printf(" **** ");
gotoxy(x,y+2); printf(" ** ");
Sleep(200);
BorrarExplosion();
gotoxy(x,y); printf(" * ** * ");
gotoxy(x,y+1); printf(" **** ");
gotoxy(x,y+2); printf(" * ** * ");
Sleep(200);
BorrarExplosion();
gotoxy(x,y); printf(" ** ");
gotoxy(x,y+1); printf(" **** ");
gotoxy(x,y+2); printf(" ** ");
Sleep(200);
BorrarExplosion();
gotoxy(x,y); printf(" * ** * ");
gotoxy(x,y+1); printf(" **** ");
gotoxy(x,y+2); printf(" * ** * ");
Sleep(200);
BorrarExplosion();
vidas--;
corazones=3;
PintarVidaSalud();
}
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada X*/
int CoordenadaX(){
return x;
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada Y*/
int CoordenadaY(){
return y;
}
/*Este método sirve para obtener el número de vidas.*/
int Vidas(){
return vidas;
}
};
/*Esta clase servirá para crear e interáctuar con los asterioides*/
class Asteroide{
private:
int x, y;
public:
/*Este método es el constructor de los objetos asteroides*/
Asteroide(int _x, int _y){
x = _x;
y = _y;
}
/*Este método se encarga de pintar los asteroides*/
void Pintar(){
gotoxy(x,y); printf("%c",184);
}
/*Este método es el encargado del movimiento de los asteroides*/
void Mover(){
gotoxy(x,y); printf(" ");
y++;
if(y > 27){
x= rand() %71 + 4;
y=4;
}
Pintar();
Sleep(5);
}
/*Este método es el encargado de detectar si el asteroide colisiona con la nave.
Se lo debemos pasar por referencia (&) ya que vamos a modificar los valores del objeto*/
void Colision(Nave &n){
if(x >= n.CoordenadaX() && x <= n.CoordenadaX()+5 && y >= n.CoordenadaY() && y < n.CoordenadaY()+2){
n.QuitaCorazones();
n.Pintar();
n.PintarVidaSalud();
Mover();
Pintar();
}
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada X*/
int CoordenadaX(){
return x;
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada Y*/
int CoordenadaY(){
return y;
}
};
/*Esta clase sirve para crear e interactuar con la bala de nuestra nave*/
class Bala{
private:
int x,y;
public:
/*Este método es el constructor del objeto bala.*/
Bala(int _x, int _y){
x = _x;
y = _y;
}
/*Este método es el encargado de mover la bala.*/
void Mover(){
gotoxy(x,y); printf(" ");
if(y > 3)
y--;
gotoxy(x,y); printf("*");
}
/*Comprueba si la bala ha llegado arriba del todo.*/
bool Fuera(){
if (y==3)
return true;
else return false;
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada X*/
int CoordenadaX(){
return x;
}
/*Este método sirve para que al llamarlo podamos obtener la cordenada Y*/
int CoordenadaY(){
return y;
}
};
int main(){
bool game_over = false;
int numero_meteoritos = 5;
int puntos = 0;
Nave nave_1(50,25,3,3,72,80,77,75); //Los cuatro ultimos numeros son los que hacen referencia a los valores ASCII de las flechas.
OcultarCursor();
nave_1.Pintar();
nave_1.PintarLimites();
nave_1.PintarVidaSalud();
/*Esto es una lista que contiente punteros a objetos de la clase Bala. Esto nos permitirá crear objetos.
También definitmos "it" lo cual nos servirá para recorrer todos los objetos que creemos y ejecutar sus acciones
ya que cada it será un puntero a un objeto. Haremos lo mismo con los meteoritos.*/
list<Bala*>B;
list<Bala*>::iterator itB;
list <Asteroide*>A;
list<Asteroide*>::iterator itA;
/*Para crear los objetos de la clase Asteroide.*/
for(int i=0; i<numero_meteoritos; i++){
A.push_back(new Asteroide(rand()%75 + 3, rand()%5+4));
}
/*Rutina de ejecución del juego. Al final colocamos la función Sleep(x) para detener el procesador
durante x milisegundos, para no forzarlo a trabajar demasiado, ya que mejora su rendimiento
y el tiempo de parada no es apreciable para los usuarios.*/
while(!game_over){
gotoxy(4,1);
printf("Puntos %d",puntos);
nave_1.Mover();
nave_1.PintarVidaSalud();
nave_1.Morir();
if(kbhit()){
char tecla = getch();
if (tecla == 'a')
/*Para crear el objeto bala, y que empiece en el centro de la nave.*/
B.push_back(new Bala(nave_1.CoordenadaX()+2, nave_1.CoordenadaY()-1));
}
/*Para recorrer los elementos de la lista. Dentro del for cada "it" actúa como
un puntero a bala, por lo que al volver a poner "*it", el puntero se desreferencia
teniendo así su contenido y no su dirección. Cada (*it) es un objeto de la clase
Bala, y al ser un puntero, llamamos al método mover con "->". Necesitamos borrar la bala
ya que se queda arriba parada. Para ello comprobamos que esté arriba para ver si tenemos que
borrarla, si está alli dibujamos un espacio, y queda así borrada. Ahora nos falta borrar ese objeto
de la memoria (usando delete), y para que el iterador no pierda el hilo de por donde
debe seguir igualamos it a la siguiente iteración mediante la llamada al método "erase".*/
for(itB=B.begin(); itB!=B.end(); itB++){
(*itB)->Mover();
if ((*itB)->Fuera()){
gotoxy((*itB)->CoordenadaX(), (*itB)->CoordenadaY());
printf(" ");
delete (*itB);
itB=B.erase(itB);
}
}
/*Recorremos todos los objetos de la clase Asteroide para moverlos y comprobar
si han colisionado con la nave.*/
for(itA=A.begin(); itA!=A.end(); itA++){
(*itA)->Mover();
(*itA)->Colision(nave_1);
}
/*Recorremos todos los objetos de la clase Asteroide, y para cada uno de ellos, recorremos los de
la clase Bala para comprobar si colisiona una bala con un meteorito. Al igual que antes al borrar
la bala debemos borrar también al puntero e igualar el it al siguiente espacio para que el iterador
no pierda el hilo. Al borrar el meteorito debemos crear otro, o llegaría un punto en el que al
destruirlos todos nos quedaríamos sin ninguno.*/
for(itA=A.begin(); itA!=A.end(); itA++){
for(itB=B.begin(); itB!=B.end(); itB++){
if((*itA)->CoordenadaX()==(*itB)->CoordenadaX() && ((*itA)->CoordenadaY()+1==(*itB)->CoordenadaY() || (*itA)->CoordenadaY()==(*itB)->CoordenadaY())){
gotoxy((*itB)->CoordenadaX(), (*itB)->CoordenadaY());
printf(" ");
delete(*itB);
itB=B.erase(itB);
A.push_back(new Asteroide(rand()%74+3, 4));
gotoxy((*itA)->CoordenadaX(), (*itA)->CoordenadaY());
printf(" ");
delete(*itA);
itA=A.erase(itA);
puntos += 5;
}
}
}
if(nave_1.Vidas() <= 0)
game_over = false;
Sleep(5);
}
return 0;
}