380 lines
11 KiB
C
380 lines
11 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ncurses.h>
|
|
#include <time.h>
|
|
|
|
/************* STATIC DEFINITIONS ************/
|
|
// Game constants
|
|
#define ENEMY_N 5
|
|
#define BULLET_N 3
|
|
#define LIVES_N 5
|
|
|
|
// Window constants
|
|
#define DELAY 30000
|
|
#define WIDTH 52
|
|
#define HEIGHT 30
|
|
#define MIN_COLS 110
|
|
|
|
// Aspect constatnts
|
|
#define TEXTLINES 4
|
|
#define BORDERLINES 2
|
|
|
|
#define ENEMY_PAIR 1
|
|
#define BONUS_PAIR 2
|
|
#define TITLE_PAIR 3
|
|
#define GMOVR_PAIR 4
|
|
|
|
/************* 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) {
|
|
// Ottieni parametri della finestra
|
|
int height;
|
|
int width;
|
|
getmaxyx(win, height, width);
|
|
|
|
// Scritta colorata
|
|
wattron(win, COLOR_PAIR(TITLE_PAIR));
|
|
for(int i = 0; i < (TEXTLINES+BORDERLINES); i++) {
|
|
mvwprintw(win, i+1, (width-(strlen(title[0])))/2, "%s", title[i]);
|
|
}
|
|
wattroff(win, COLOR_PAIR(TITLE_PAIR));
|
|
|
|
// Scritta che blinka
|
|
wattron(win, A_BLINK);
|
|
mvwprintw(win, (TEXTLINES+BORDERLINES)+1, (width-(strlen(menu_desc)))/2, "%s", menu_desc);
|
|
wattroff(win, A_BLINK);
|
|
|
|
// stampa a video
|
|
refresh();
|
|
return;
|
|
}
|
|
|
|
void print_gameover(WINDOW* win) {
|
|
// Parametri della finestra e pulisci tutto
|
|
int height;
|
|
int width;
|
|
int ch;
|
|
wclear(win);
|
|
getmaxyx(win, height, width);
|
|
|
|
// Stampa della scritta colorata
|
|
wattron(win, COLOR_PAIR(GMOVR_PAIR));
|
|
for(int i = 0; i < TEXTLINES; i++) {
|
|
mvwprintw(win, i+1, (width-(strlen(gameover[0])))/2, "%s", gameover[i]);
|
|
}
|
|
wattroff(win, COLOR_PAIR(GMOVR_PAIR));
|
|
|
|
// Stampa della scritta che blinka
|
|
wattron(win, A_BLINK);
|
|
mvwprintw(win, TEXTLINES+1, (width-(strlen(gameover_desc)))/2, "%s", gameover_desc);
|
|
wattroff(win, A_BLINK);
|
|
|
|
// Stampa a video e attendi input
|
|
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));
|
|
|
|
// Colori
|
|
start_color();
|
|
init_pair(ENEMY_PAIR, COLOR_RED, COLOR_BLACK);
|
|
init_pair(TITLE_PAIR, COLOR_MAGENTA, COLOR_BLACK);
|
|
init_pair(GMOVR_PAIR, COLOR_RED, COLOR_BLACK);
|
|
init_pair(BONUS_PAIR, COLOR_GREEN, COLOR_BLACK);
|
|
|
|
// 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) {
|
|
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++) {
|
|
wattron(game_window, COLOR_PAIR(1));
|
|
if (enemy[i].active) mvwprintw(game_window, enemy[i].y, enemy[i].x, "%c", enemy[i].symbol);
|
|
wattroff(game_window, COLOR_PAIR(1));
|
|
}
|
|
|
|
|
|
// 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;
|
|
}
|