space-shooter/main.c

343 lines
10 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ncurses.h>
#include <time.h>
/************* STATIC DEFINITIONS ************/
#define DELAY 30000
#define ENEMY_N 5
#define BULLET_N 3
#define WIDTH 52
#define HEIGHT 30
#define MIN_COLS 110
#define LIVES_N 5
#define TEXTLINES 4
#define BORDERLINES 2
/************* GAME STRUCTURES **************/
struct speed {
int slowness;
int count;
};
struct game_obj {
char symbol;
int x, y;
int active;
struct speed y_speed;
};
struct status {
int lives;
int score;
int top_score;
int remaining_bullets;
};
/************** FUNZIONI GUI ***************/
char* title[] =
{
"###########################################################################################################",
"# o8888o o8888o o8888o o8888o o8888o o8888o o8 80 o8888o o8888o o8888o o8888o o8888o #",
"# 888o 0oooo0 88 888 88 88oo oo 888o 888o88 88 888 88 888 88 88oo 88oo88 #",
"# o888 88 888888 88 88 o888 88 88 88 888 88 888 88 88 88 88 #",
"# 088880 88 88 88 888880 088888 088880 08 80 088880 088880 00 088888 88 88o #",
"###########################################################################################################"
};
char menu_desc[] = "BENVENUTO. Premi qualsiasi tasto per giocare...";
char* gameover[] =
{
"o8888o o8888o o8oo8o o8888o o8888o 88 8o o8888o o8888o",
"888 oo 88 888 88\\/88 88oo oo 88 888 88 88 88oo 88oo88",
"888 8 888888 88 88 88 88 888 88 o8 88 88 88 ",
"O88880 88 88 88 88 888880 088880 888O 888880 88 88o"
};
char gameover_desc[] = "HAI PERSO. Premi q per uscire...";
void print_menu(WINDOW* win) {
int height;
int width;
getmaxyx(win, height, width);
for(int i = 0; i < (TEXTLINES+BORDERLINES); i++) {
mvwprintw(win, i+1, (width-(strlen(title[0])))/2, "%s", title[i]);
}
wattron(win, A_BLINK);
mvwprintw(win, (TEXTLINES+BORDERLINES)+1, (width-(strlen(menu_desc)))/2, "%s", menu_desc);
wattroff(win, A_BLINK);
refresh();
return;
}
void print_gameover(WINDOW* win) {
int height;
int width;
int ch;
wclear(win);
getmaxyx(win, height, width);
for(int i = 0; i < TEXTLINES; i++) {
mvwprintw(win, i+1, (width-(strlen(gameover[0])))/2, "%s", gameover[i]);
}
wattron(win, A_BLINK);
mvwprintw(win, TEXTLINES+1, (width-(strlen(gameover_desc)))/2, "%s", gameover_desc);
wattroff(win, A_BLINK);
refresh();
while((ch = wgetch(win)) != 'q');
return;
}
void print_status_bar(WINDOW* main_win, int y, int x, struct status game_status) {
int status_x = x + 1;
int status_y = y;
status_x++;
// lives
mvwprintw(main_win, status_y, status_x, "LIVES:\t%d", game_status.lives);
// score
mvwprintw(main_win, status_y+1, status_x, "SCORE:\t%d", game_status.score);
// top score
mvwprintw(main_win, status_y+2, status_x, "TOP SCORE:\t%d", game_status.top_score);
// bullets
mvwprintw(main_win, status_y+3, status_x, "BULLETS:\t%d", game_status.remaining_bullets);
// refresh window
wrefresh(main_win);
return;
}
int main() {
/************ VARIABILI **************/
WINDOW *game_window;
int height, width, win_startx, win_starty;
char *pause_msg = "PAUSA: premi q per uscire dal gioco...";
int pause = 0;
int auto_shoot = 0;
// Giocatore
struct game_obj player = {};
int input;
// Status
struct status game_status = {};
game_status.top_score = 0;
game_status.score = 0;
game_status.lives = LIVES_N;
game_status.remaining_bullets = BULLET_N;
// Proiettile
struct game_obj bullet[BULLET_N] = {};
int bullet_attivi = 0;
int next_bullet = 0;
// Nemici
struct game_obj enemy[ENEMY_N] = {};
/**************** INIZIALIZZAZIONE ***************/
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
curs_set(0);
srand(time(NULL));
// Dimensioni finestra di gioco e controlli
// calcolo delle misure della finestra
height = HEIGHT;
width = WIDTH - (WIDTH%4);
win_startx = (COLS - width)/2;
win_starty = (LINES - height)/2;
// Schermo troppo piccolo ?
if(COLS < MIN_COLS || LINES < HEIGHT) {
delwin(game_window);
curs_set(1);
endwin();
printf("Gioco terminato.\n");
printf("Schermo troppo piccolo \n");
return 0;
}
// MENU
print_menu(stdscr);
getch();
werase(stdscr);
mvprintw(1, 1, "p per pausa, q per uscire");
refresh();
// GAME WINDOW
game_window = newwin(height, width, win_starty, win_startx);
nodelay(game_window, TRUE); // input non bloccante
keypad(game_window, TRUE);
box(game_window, 0, 0);
wrefresh(game_window);
// setup giocatore
player.symbol = 'A';
player.x = width / 2;
player.y = height - 2;
player.active = 1;
// setup proiettili
for(int i = 0; i < BULLET_N; i++) {
bullet[i].symbol = '*';
bullet[i].active = 0;
}
// setup dei nemici
for(int i = 0; i < ENEMY_N; i++) {
enemy[i].symbol = 'Q';
enemy[i].active = 0;
enemy[i].y_speed.slowness = 7;
enemy[i].y_speed.count = 0;
}
//********************* GAME LOOP ***********************/
while (player.active) {
// TODO if pausa all'inizio e continue
input = wgetch(game_window);
// Gestione input
if (input == KEY_RIGHT && !pause && player.x < width - 2) {
player.x += 2;
} else if (input == KEY_LEFT && !pause && player.x > 2) {
player.x -= 2;
} else if (!auto_shoot && input == ' ' && bullet_attivi < BULLET_N && !pause) {
bullet[next_bullet].x = player.x;
bullet[next_bullet].y = player.y - 1;
bullet[next_bullet].active = 1;
} else if (input == 'p') {
pause = !pause;
} else if (input == 'q') {
player.active = 0;
} else if (input == 'g') {
auto_shoot= !auto_shoot;
}
if (auto_shoot && bullet_attivi < BULLET_N && !pause) {
bullet[next_bullet].x = player.x;
bullet[next_bullet].y = player.y - 1;
bullet[next_bullet].active = 1;
}
// Aggiorna proiettile
for(int i = 0; i< BULLET_N; i++) {
if (bullet[i].active && !pause) {
bullet[i].y--;
}
if (bullet[i].y < 1) {
bullet[i].active = 0;
}
}
// Collisioni 1
for(int j = 0; j < BULLET_N; j++) {
if(bullet[j].active && !pause) {
for(int i = 0; i < ENEMY_N; i++) {
if(bullet[j].x == enemy[i].x && bullet[j].y == enemy[i].y) {
enemy[i].active = 0;
bullet[j].active = 0;
game_status.score++;
}
}
}
}
// Spawna nemici
for(int i = 0; !pause && i < ENEMY_N; i++) {
if(!enemy[i].active && !pause) {
enemy[i].y = 2;
enemy[i].x = (rand()%((width-4)/2))*2 + 2;
enemy[i].active = 1;
break;
}
}
// Aggiorna posizione nemici
for(int i = 0; !pause && i < ENEMY_N; i++) {
if(enemy[i].active && !pause) {
enemy[i].y_speed.count++;
if(enemy[i].y_speed.count > enemy[i].y_speed.slowness) {
enemy[i].y++;
enemy[i].y_speed.count = 0;
}
if(enemy[i].y > height - 2) {
enemy[i].active = 0;
game_status.lives--;
if(game_status.lives == 0) {
player.active = 0;
print_gameover(stdscr);
}
}
}
}
// Collisioni 2
for(int j = 0; j < BULLET_N; j++) {
if(bullet[j].active && !pause) {
for(int i = 0; i < ENEMY_N; i++) {
if(bullet[j].x == enemy[i].x && bullet[j].y == enemy[i].y) {
enemy[i].active = 0;
bullet[j].active = 0;
game_status.score++;
}
}
}
}
// Conteggio proiettili
bullet_attivi = 0;
next_bullet = -1;
for (int i = 0; i < BULLET_N; i++) {
if (bullet[i].active) {
bullet_attivi++;
} else if (i < next_bullet || next_bullet == -1) {
next_bullet = i;
}
}
game_status.remaining_bullets = BULLET_N - bullet_attivi;
// Disegna scena
werase(game_window);
box(game_window, 0, 0);
if (pause) {
wattron(game_window, A_REVERSE);
mvwprintw(game_window, height/2, (width - strlen(pause_msg)) / 2, "%s", pause_msg);
wattroff(game_window, A_REVERSE);
} else {
// Giocatore
mvwprintw(game_window, player.y, player.x, "%c", player.symbol);
// Proiettile
for(int i = 0; i < BULLET_N; i++) {
if (bullet[i].active) mvwprintw(game_window, bullet[i].y, bullet[i].x, "%c", bullet[i].symbol);
}
// Nemici
for(int i = 0; i < ENEMY_N; i++) {
if (enemy[i].active) mvwprintw(game_window, enemy[i].y, enemy[i].x, "%c", enemy[i].symbol);
}
// Status Bar
print_status_bar(stdscr, win_starty, (win_startx + width), game_status);
}
wrefresh(game_window);
usleep(DELAY);
}
/***************** CHIUSURA *****************/
delwin(game_window);
curs_set(1);
endwin();
printf("Gioco terminato.\n");
return 0;
}