/* 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); }