/*
Copyright 2007 Tony Clay
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#define TITLE_COLOUR_1 1
#define TITLE_COLOUR_2 2
#define STATUS_COLOUR 3
#define SHIP_COLOUR 4
#define MISSLE_COLOUR 5
#define DEAD_COLOUR 6
#define BUBBLE_COLOUR_1 7
#define BUBBLE_COLOUR_2 8
#define BUBBLE_COLOUR_3 9
#define BUBBLE_COLOUR_4 10
#define BUBBLE_COLOUR_5 11
#define BUBBLE_COLOUR_6 12
#define BUBBLE_COLOUR_7 13
#define SHIP_MISSLE_POS 2
#define SHIP_WIDTH 5
#define BUBBLE_WIDTH 7
#define BUBBLE_HEIGHT 4
#define MAX_BUBBLES 20
#define MAX_MISSLES 20
#define MAX_BUBBLE_SPEED 4
#define BUBBLE_CHANGE_SPEED_CHANCE 10
#define MISSLE_SPEED 1
#define MISSLE_FIRE_RATE 25
#define MIN_POS_Y 1
#define MIN_POS_X 0
#define DELAY 10000
typedef struct {
bool blown;
int xPos;
int yPos;
int old_xPos;
int old_yPos;
int xDir;
int yDir;
int movedY;
int movedX;
int speedY;
int speedX;
int colour;
} bubble_struct;
typedef struct {
int yPos;
int xPos;
int old_yPos;
int old_xPos;
int missleFired;
} ship_struct;
typedef struct {
bool fired;
int xPos;
int yPos;
int old_xPos;
int old_yPos;
int moved;
} missle_struct;
int randNum(int maxValue) {
return rand() % (maxValue+1);
}
void initBubbleList(bubble_struct bubbleList[]) {
int bubbleInd;
for (bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubbleList[bubbleInd].blown = FALSE;
}
}
void initShip(ship_struct *ship) {
int maxPos_y, maxPos_x;
getmaxyx(stdscr, maxPos_y, maxPos_x);
ship->xPos = maxPos_x / 2;
ship->yPos = maxPos_y-1;
ship->old_xPos = -1;
ship->old_yPos = -1;
ship->missleFired = 0;
}
void initMissleList(missle_struct missleList[]) {
int missleInd;
for (missleInd = 0; missleInd < MAX_MISSLES; missleInd++) {
missleList[missleInd].fired = FALSE;
}
}
void moveMissle(missle_struct missleList[]) {
int missleInd;
missle_struct *missle;
for(missleInd = 0; missleInd < MAX_MISSLES; missleInd++) {
missle = missleList + missleInd;
if (missle->fired) {
if (missle->moved > 0) {
missle->moved--;
}
else {
missle->yPos--;
if (missle->yPos < MIN_POS_Y) {
missle->fired = FALSE;
}
missle->moved = MISSLE_SPEED;
}
}
}
}
void moveBubbleAxis(int *pos, int *dir, int *speed, int *moved, int minPos,
int maxPos) {
if (*moved > 0) {
(*moved)--;
}
else {
if ((*dir > 0 && *pos >= maxPos) ||
(*dir < 0 && *pos <= minPos)) {
*dir = -*dir;
}
else if (*speed > 0) {
*pos += *dir;
}
if (randNum(100) <= BUBBLE_CHANGE_SPEED_CHANCE) {
if (*speed == 0) {
*speed = 1;
if (randNum(1) == 0) *dir = -*dir;
}
else if (*speed == MAX_BUBBLE_SPEED) {
(*speed)--;
}
else if (randNum(1) == 0) {
(*speed)--;
}
else {
(*speed)++;
}
}
*moved = *speed;
}
}
void moveBubble(bubble_struct bubbleList[]) {
int bubbleInd;
bubble_struct *bubble;
int maxPosY, maxPosX;
getmaxyx(stdscr, maxPosY, maxPosX);
for(bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubble = bubbleList + bubbleInd;
if (bubble->blown) {
moveBubbleAxis(&bubble->yPos, &bubble->yDir, &bubble->speedY,
&bubble->movedY, MIN_POS_Y, maxPosY-BUBBLE_HEIGHT);
moveBubbleAxis(&bubble->xPos, &bubble->xDir, &bubble->speedX,
&bubble->movedX, MIN_POS_X, maxPosX-BUBBLE_WIDTH);
}
}
}
void popBubble(bubble_struct *bubble, int *numBubblesPopped) {
bubble->blown = FALSE;
bubble->yPos = -1;
(*numBubblesPopped)++;
beep();
}
void checkCollisions(bubble_struct bubbleList[], ship_struct *ship,
missle_struct missleList[], int *numBubblesPopped) {
int bubbleInd;
int missleInd;
missle_struct *missle;
bubble_struct *bubble;
for(bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubble = bubbleList + bubbleInd;
if (bubble->blown) {
if (ship->yPos >= bubble->yPos &&
ship->yPos <= bubble->yPos+BUBBLE_HEIGHT-1 &&
ship->xPos+SHIP_WIDTH-1 >= bubble->xPos &&
ship->xPos <= bubble->xPos+BUBBLE_WIDTH-1) {
popBubble(bubble, numBubblesPopped);
}
else {
for(missleInd = 0; missleInd < MAX_MISSLES; missleInd++) {
missle = missleList + missleInd;
if (missle->fired) {
if (missle->yPos >= bubble->yPos &&
missle->yPos <= bubble->yPos+BUBBLE_HEIGHT-1 &&
missle->xPos >= bubble->xPos &&
missle->xPos <= bubble->xPos+BUBBLE_WIDTH-1) {
popBubble(bubble, numBubblesPopped);
missle->fired = FALSE;
missle->yPos = -1;
}
}
}
}
}
}
}
void writeScore(int numBubblesPopped) {
attroff(A_BOLD);
attron(COLOR_PAIR(STATUS_COLOUR));
mvprintw(0, 0, "You have popped %i bubble%s. ", numBubblesPopped,
numBubblesPopped == 1 ? "" : "s");
}
void rubOutShip(ship_struct *ship) {
if (ship->old_xPos != ship->xPos ||
ship->old_yPos != ship->yPos) {
mvprintw(ship->old_yPos, ship->old_xPos, " ");
ship->old_xPos = ship->xPos;
ship->old_yPos = ship->yPos;
}
}
void rubOutMissles(missle_struct missleList[]) {
int missleInd;
missle_struct *missle;
for (missleInd = 0; missleInd < MAX_MISSLES; missleInd++) {
missle = missleList + missleInd;
if (missle->yPos != missle->old_yPos ||
missle->xPos != missle->old_xPos) {
if (missle->old_yPos != -1) {
mvprintw(missle->old_yPos, missle->old_xPos, " ");
}
}
}
}
void rubOutBubbles(bubble_struct bubbleList[]) {
int bubbleInd;
bubble_struct *bubble;
for(bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubble = bubbleList + bubbleInd;
if (bubble->yPos != bubble->old_yPos ||
bubble->xPos != bubble->old_xPos) {
if (bubble->old_yPos != -1) {
mvprintw(bubble->old_yPos, bubble->old_xPos+2, " ");
mvprintw(bubble->old_yPos+1, bubble->old_xPos+1, " ");
mvprintw(bubble->old_yPos+1, bubble->old_xPos+5, " ");
mvprintw(bubble->old_yPos+2, bubble->old_xPos, " ");
mvprintw(bubble->old_yPos+2, bubble->old_xPos+6, " ");
mvprintw(bubble->old_yPos+3, bubble->old_xPos+1, " ");
}
}
}
}
void drawMissles(missle_struct missleList[]) {
int missleInd;
missle_struct *missle;
attron(A_BOLD);
attron(COLOR_PAIR(MISSLE_COLOUR));
for (missleInd = 0; missleInd < MAX_MISSLES; missleInd++) {
missle = missleList + missleInd;
if (missle->yPos != missle->old_yPos ||
missle->xPos != missle->old_xPos) {
if (missle->fired) {
mvprintw(missle->yPos, missle->xPos, "^");
}
missle->old_yPos = missle->yPos;
missle->old_xPos = missle->xPos;
}
}
}
void drawBubbles(bubble_struct bubbleList[]) {
int bubbleInd;
bubble_struct *bubble;
for(bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubble = bubbleList + bubbleInd;
if (bubble->yPos != bubble->old_yPos ||
bubble->xPos != bubble->old_xPos) {
if (bubble->blown) {
if (bubble->colour == 0 || randNum(4) < 1) {
bubble->colour = BUBBLE_COLOUR_1 + randNum(6);
}
attron(COLOR_PAIR(bubble->colour));
mvprintw(bubble->yPos, bubble->xPos+2, "%s", "___");
mvprintw(bubble->yPos+1, bubble->xPos+1, "%s", "/");
mvprintw(bubble->yPos+1, bubble->xPos+5, "%s", "\\");
mvprintw(bubble->yPos+2, bubble->xPos, "%s", "|");
mvprintw(bubble->yPos+2, bubble->xPos+6, "%s", "|");
mvprintw(bubble->yPos+3, bubble->xPos+1, "%s", "\\___/");
}
bubble->old_yPos = bubble->yPos;
bubble->old_xPos = bubble->xPos;
}
}
}
void drawShip(ship_struct *ship) {
attron(COLOR_PAIR(SHIP_COLOUR));
mvprintw(ship->yPos, ship->xPos, "/- -\\");
if (!ship->missleFired) {
attron(COLOR_PAIR(MISSLE_COLOUR));
mvprintw(ship->yPos, ship->xPos+SHIP_MISSLE_POS, "^");
}
}
void drawScreen(bubble_struct bubbleList[], ship_struct *ship,
missle_struct missleList[], int numBubblesPopped) {
writeScore(numBubblesPopped);
rubOutShip(ship);
rubOutMissles(missleList);
rubOutBubbles(bubbleList);
drawMissles(missleList);
drawBubbles(bubbleList);
drawShip(ship);
refresh();
}
void blowBubble(bubble_struct bubbleList[]) {
int maxPos_x, maxPos_y;
int bubbleInd;
bubble_struct *bubble;
for (bubbleInd = 0; bubbleInd < MAX_BUBBLES; bubbleInd++) {
bubble = bubbleList + bubbleInd;
if (!bubble->blown) {
bubble->blown = TRUE;
getmaxyx(stdscr, maxPos_y, maxPos_x);
bubble->xPos = randNum(maxPos_x - MIN_POS_X
- BUBBLE_WIDTH) + MIN_POS_X;
bubble->yPos = randNum(maxPos_y - MIN_POS_Y
- BUBBLE_HEIGHT) + MIN_POS_Y;
if (randNum(1) == 1) bubble->xDir = 1;
else bubble->xDir = -1;
if (randNum(1) == 1) bubble->yDir = 1;
else bubble->yDir = -1;
bubble->speedY = randNum(MAX_BUBBLE_SPEED);
bubble->speedX = randNum(MAX_BUBBLE_SPEED);
bubble->movedY = 0;
bubble->movedX = 0;
bubble->colour = 0;
bubble->old_yPos = -1;
bubble->old_xPos = -1;
break;
}
}
}
void fireMissle(ship_struct *ship, missle_struct missleList[]) {
int missleInd;
missle_struct *missle;
for (missleInd = 0; missleInd < MAX_BUBBLES; missleInd++) {
missle = missleList + missleInd;
if (!missle->fired) {
missle->fired = TRUE;
missle->yPos = ship->yPos;
missle->xPos = ship->xPos + SHIP_MISSLE_POS;
missle->old_yPos = -1;
missle->old_xPos = -1;
missle->moved = 0;
ship->missleFired = MISSLE_FIRE_RATE;
break;
}
}
}
int processInput(bubble_struct bubbleList[], ship_struct *ship,
missle_struct missleList[]) {
int quitRequested = 0;
int inChar = getch();
int maxPos_y, maxPos_x;
switch (inChar) {
case KEY_LEFT:
if (ship->xPos > MIN_POS_X) {
ship->xPos--;
}
break;
case KEY_RIGHT:
getmaxyx(stdscr, maxPos_y, maxPos_x);
if ( ship->xPos < maxPos_x-SHIP_WIDTH) {
ship->xPos++;
}
break;
case KEY_UP:
if (ship->yPos > MIN_POS_Y) {
ship->yPos--;
}
break;
case KEY_DOWN:
getmaxyx(stdscr, maxPos_y, maxPos_x);
if (ship->yPos < maxPos_y-1) {
ship->yPos++;
}
break;
case ' ':
if (!ship->missleFired) {
fireMissle(ship, missleList);
}
break;
case 'b':
case 'B':
blowBubble(bubbleList);
break;
case 'q':
case 'Q':
quitRequested = 1;
break;
}
return quitRequested;
}
void gameOverMessage() {
int maxPosY, maxPosX;
getmaxyx(stdscr, maxPosY, maxPosX);
move(maxPosY / 2, (maxPosX - 18) / 2);
attron(COLOR_PAIR(TITLE_COLOUR_1));
printw("G ");
attron(COLOR_PAIR(TITLE_COLOUR_2));
printw("A ");
attron(COLOR_PAIR(TITLE_COLOUR_1));
printw("M ");
attron(COLOR_PAIR(TITLE_COLOUR_2));
printw("E ");
attron(COLOR_PAIR(TITLE_COLOUR_1));
printw("O ");
attron(COLOR_PAIR(TITLE_COLOUR_2));
printw("V ");
attron(COLOR_PAIR(TITLE_COLOUR_1));
printw("E ");
attron(COLOR_PAIR(TITLE_COLOUR_2));
printw("R ");
refresh();
}
void playGame() {
bubble_struct bubbleList[MAX_BUBBLES];
ship_struct ship;
missle_struct missleList[MAX_MISSLES];
int numBubblesPopped = 0;
initBubbleList(bubbleList);
initShip(&ship);
initMissleList(missleList);
clear();
int quitRequested = 0;
while (!quitRequested) {
if (ship.missleFired > 0) ship.missleFired--;
moveBubble(bubbleList);
moveMissle(missleList);
checkCollisions(bubbleList, &ship, missleList, &numBubblesPopped);
drawScreen(bubbleList, &ship, missleList, numBubblesPopped);
if (usleep((unsigned int) DELAY) == -1 ) quitRequested = 1;
if (!quitRequested) quitRequested = processInput(bubbleList, &ship,
missleList);
}
gameOverMessage();
sleep(3);
}
void title(int *yPos, int *xPos) {
int maxPosY, maxPosX;
getmaxyx(stdscr, maxPosY, maxPosX);
*yPos = 2;
*xPos = (maxPosX - 76) / 2;
if (*xPos < 0) *xPos = 0;
attroff(A_BOLD);
attron(COLOR_PAIR(TITLE_COLOUR_1));
mvprintw((*yPos)++, *xPos, " _ _ _ _ _ _");
mvprintw((*yPos)++, *xPos, " _ __ ___ _ __ | |_| |__ ___ | |__ _ _| |__ | |__ | | ___ ___");
mvprintw((*yPos)++, *xPos, "| '_ \\ / _ \\| '_ \\ | __| '_ \\ / _ \\ | '_ \\| | | | '_ \\| '_ \\| |/ _ \\/ __|");
mvprintw((*yPos)++, *xPos, "| |_) | (_) | |_) | | |_| | | | __/ | |_) | |_| | |_) | |_) | | __/\\__ \\");
mvprintw((*yPos)++, *xPos, "| .__/ \\___/| .__/ \\__|_| |_|\\___| |_.__/ \\__,_|_.__/|_.__/|_|\\___||___/");
mvprintw((*yPos)++, *xPos, "|_| |_|");
}
void startScreen() {
int yPos, xPos;
clear();
title(&yPos, &xPos);
attron(COLOR_PAIR(TITLE_COLOUR_2));
yPos += 2;
xPos += 30;
mvprintw(yPos++, xPos, "Press S to start");
xPos += 6;
mvprintw(yPos++, xPos, "I for instructions");
mvprintw(yPos, xPos, "Q to exit");
refresh();
}
void instructions() {
int yPos, xPos;
clear();
title(&yPos, &xPos);
attron(COLOR_PAIR(TITLE_COLOUR_2));
yPos += 2;
xPos += 6;
mvprintw(yPos++, xPos,
"The object of the game is to blow bubbles and then pop them!");
mvprintw(yPos++, xPos,
"You can pop bubbles by either firing a missle at them or");
mvprintw(yPos++, xPos,
"crashing your ship into them.");
yPos++;
mvprintw(yPos++, xPos,
"Keys: B - Blow a bubble.");
xPos += 6;
mvprintw(yPos++, xPos,
"Arrow Keys - Move ship.");
mvprintw(yPos++, xPos,
"Space - Fire missle.");
yPos++;
xPos += 10;
mvprintw(yPos, xPos, "Press any key to continue.");
refresh();
getch();
}
int main() {
initscr();
noecho();
curs_set(0);
keypad(stdscr, TRUE);
start_color();
init_pair(TITLE_COLOUR_1, COLOR_CYAN, COLOR_BLACK);
init_pair(TITLE_COLOUR_2, COLOR_MAGENTA, COLOR_BLACK);
init_pair(STATUS_COLOUR, COLOR_YELLOW, COLOR_BLACK);
init_pair(SHIP_COLOUR, COLOR_GREEN, COLOR_BLACK);
init_pair(MISSLE_COLOUR, COLOR_YELLOW, COLOR_BLACK);
init_pair(DEAD_COLOUR, COLOR_RED, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_1, COLOR_CYAN, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_2, COLOR_MAGENTA, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_3, COLOR_YELLOW, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_4, COLOR_GREEN, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_5, COLOR_BLUE, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_6, COLOR_RED, COLOR_BLACK);
init_pair(BUBBLE_COLOUR_7, COLOR_WHITE, COLOR_BLACK);
int exitRequested = 0;
while (!exitRequested) {
startScreen();
nodelay(stdscr, FALSE);
int inChar = getch();
switch (inChar) {
case 's':
case 'S':
nodelay(stdscr, TRUE);
playGame();
break;
case 'i':
case 'I':
instructions();
break;
case 'q' :
case 'Q' :
exitRequested = 1;
break;
}
}
endwin();
exit(EXIT_SUCCESS);
}