diff options
author | Matthew Jordan <mjordan@digium.com> | 2014-07-17 18:42:43 +0000 |
---|---|---|
committer | Matthew Jordan <mjordan@digium.com> | 2014-07-17 18:42:43 +0000 |
commit | c7d3570248cfee32fb35fdf9d683ccb23e8da10b (patch) | |
tree | 792b25aea89fbbebf8eee3914037448729760cc1 /menuselect/menuselect_curses.c | |
parent | cd6c7744568e07124bd2cbf01d57eb0e15c82013 (diff) |
menuselect: Add menuselect to Asterisk trunk (Patch 1)
This is the first patch that adds menuselect to Asterisk trunk, and removes
the svn:externals property. This is being done for two reasons:
(1) The removal of external repositories eases a future migration to git
(2) Asterisk is now the only thing that uses menuselect; as a result, there's
little need to keep it in an external repository
Subsequent patches will remove the mxml dependency from menuselect and tidy
up the build system.
ASTERISK-20703
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@418832 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'menuselect/menuselect_curses.c')
-rw-r--r-- | menuselect/menuselect_curses.c | 1034 |
1 files changed, 1034 insertions, 0 deletions
diff --git a/menuselect/menuselect_curses.c b/menuselect/menuselect_curses.c new file mode 100644 index 000000000..5afa99661 --- /dev/null +++ b/menuselect/menuselect_curses.c @@ -0,0 +1,1034 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2006, Russell Bryant + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * \file + * + * \author Russell Bryant <russell@digium.com> + * + * \brief curses frontend for selection maintenance + */ + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <curses.h> + +#include "menuselect.h" + +#define MENU_HELP "Press 'h' for help." + +#define TITLE_HEIGHT 7 + +#define MIN_X 80 +#define MIN_Y 27 + +#define PAGE_OFFSET 10 + +#define SCROLL_NONE 0 +#define SCROLL_DOWN 1 + +#define SCROLL_DOWN_INDICATOR "... More ..." + +#define MIN(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a > __b) ? __b : __a);}) +#define MAX(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a < __b) ? __b : __a);}) + +extern int changes_made; + +/*! Maximum number of characters horizontally */ +static int max_x = 0; +/*! Maximum number of characters vertically */ +static int max_y = 0; + +static const char * const help_info[] = { + "scroll => up/down arrows", + "toggle selection => Enter", + "select => y", + "deselect => n", + "select all => F8", + "deselect all => F7", + "back => left arrow", + "quit => q", + "save and quit => x", + "", + "XXX means dependencies have not been met", + " or a conflict exists", + "", + "< > means a dependency has been deselected", + " and will be automatically re-selected", + " if this item is selected", + "", + "( ) means a conflicting item has been", + " selected", +}; + +/*! \brief Handle a window resize in xterm */ +static void _winch_handler(int sig) +{ + getmaxyx(stdscr, max_y, max_x); + + if (max_x < MIN_X || max_y < MIN_Y) { + fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y); + max_x = MIN_X - 1; + max_y = MIN_Y - 1; + } +} + +static struct sigaction winch_handler = { + .sa_handler = _winch_handler, +}; + +/*! \brief Handle a SIGQUIT */ +static void _sigint_handler(int sig) +{ + +} + +static struct sigaction sigint_handler = { + .sa_handler = _sigint_handler, +}; + +/*! \brief Display help information */ +static void show_help(WINDOW *win) +{ + int i; + + wclear(win); + for (i = 0; i < (sizeof(help_info) / sizeof(help_info[0])); i++) { + wmove(win, i, max_x / 2 - 15); + waddstr(win, (char *) help_info[i]); + } + wrefresh(win); + getch(); /* display the help until the user hits a key */ +} + +static int really_quit(WINDOW *win) +{ + int c; + wclear(win); + wmove(win, 2, max_x / 2 - 15); + waddstr(win, "ARE YOU SURE?"); + wmove(win, 3, max_x / 2 - 12); + waddstr(win, "--- It appears you have made some changes, and"); + wmove(win, 4, max_x / 2 - 12); + waddstr(win, "you have opted to Quit without saving these changes!"); + wmove(win, 6, max_x / 2 - 12); + waddstr(win, " Please Enter Y to exit without saving;"); + wmove(win, 7, max_x / 2 - 12); + waddstr(win, " Enter N to cancel your decision to quit,"); + wmove(win, 8, max_x / 2 - 12); + waddstr(win, " and keep working in menuselect, or"); + wmove(win, 9, max_x / 2 - 12); + waddstr(win, " Enter S to save your changes, and exit"); + wmove(win, 10, max_x / 2 - 12); + wrefresh(win); + while ((c=getch())) { + if (c == 'Y' || c == 'y') { + c = 'q'; + break; + } + if (c == 'S' || c == 's') { + c = 'S'; + break; + } + if (c == 'N' || c == 'n') { + c = '%'; + break; + } + } + return c; +} + +static void draw_main_menu(WINDOW *menu, int curopt) +{ + struct category *cat; + char buf[64]; + int i = 0; + + wclear(menu); + + AST_LIST_TRAVERSE(&categories, cat, list) { + wmove(menu, i++, max_x / 2 - 10); + snprintf(buf, sizeof(buf), " %s", strlen_zero(cat->displayname) ? cat->name : cat->displayname); + waddstr(menu, buf); + } + + wmove(menu, curopt, (max_x / 2) - 15); + waddstr(menu, "--->"); + wmove(menu, 0, 0); + + wrefresh(menu); +} + +static void display_mem_info(WINDOW *menu, struct member *mem, int start, int end) +{ + char buf[64]; + struct reference *dep; + struct reference *con; + struct reference *use; + + wmove(menu, end - start + 2, max_x / 2 - 16); + wclrtoeol(menu); + wmove(menu, end - start + 3, max_x / 2 - 16); + wclrtoeol(menu); + wmove(menu, end - start + 4, max_x / 2 - 16); + wclrtoeol(menu); + wmove(menu, end - start + 5, max_x / 2 - 16); + wclrtoeol(menu); + wmove(menu, end - start + 6, max_x / 2 - 16); + wclrtoeol(menu); + + if (mem->displayname) { + wmove(menu, end - start + 2, max_x / 2 - 16); + waddstr(menu, (char *) mem->displayname); + } + if (!AST_LIST_EMPTY(&mem->deps)) { + wmove(menu, end - start + 3, max_x / 2 - 16); + strcpy(buf, "Depends on: "); + AST_LIST_TRAVERSE(&mem->deps, dep, list) { + strncat(buf, dep->displayname, sizeof(buf) - strlen(buf) - 1); + strncat(buf, dep->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1); + if (AST_LIST_NEXT(dep, list)) + strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1); + } + waddstr(menu, buf); + } + if (!AST_LIST_EMPTY(&mem->uses)) { + wmove(menu, end - start + 4, max_x / 2 - 16); + strcpy(buf, "Can use: "); + AST_LIST_TRAVERSE(&mem->uses, use, list) { + strncat(buf, use->displayname, sizeof(buf) - strlen(buf) - 1); + strncat(buf, use->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1); + if (AST_LIST_NEXT(use, list)) + strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1); + } + waddstr(menu, buf); + } + if (!AST_LIST_EMPTY(&mem->conflicts)) { + wmove(menu, end - start + 5, max_x / 2 - 16); + strcpy(buf, "Conflicts with: "); + AST_LIST_TRAVERSE(&mem->conflicts, con, list) { + strncat(buf, con->displayname, sizeof(buf) - strlen(buf) - 1); + strncat(buf, con->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1); + if (AST_LIST_NEXT(con, list)) + strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1); + } + waddstr(menu, buf); + } + + if (!mem->is_separator) { /* Separators lack support levels */ + { /* support level */ + wmove(menu, end - start + 6, max_x / 2 - 16); + snprintf(buf, sizeof(buf), "Support Level: %s", mem->support_level); + if (mem->replacement && *mem->replacement) { + char buf2[64]; + snprintf(buf2, sizeof(buf2), ", Replaced by: %s", mem->replacement); + strncat(buf, buf2, sizeof(buf) - strlen(buf) - 1); + } + waddstr(menu, buf); + } + } +} + +static void draw_category_menu(WINDOW *menu, struct category *cat, int start, int end, int curopt, int changed, int flags) +{ + int i = 0; + int j = 0; + struct member *mem; + char buf[64]; + + if (!changed) { + /* If all we have to do is move the cursor, + * then don't clear the screen and start over */ + AST_LIST_TRAVERSE(&cat->members, mem, list) { + i++; + if (curopt + 1 == i) { + display_mem_info(menu, mem, start, end); + break; + } + } + wmove(menu, curopt - start, max_x / 2 - 9); + wrefresh(menu); + return; + } + + wclear(menu); + + i = 0; + AST_LIST_TRAVERSE(&cat->members, mem, list) { + if (i < start) { + i++; + continue; + } + wmove(menu, j++, max_x / 2 - 10); + i++; + if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE)) { + snprintf(buf, sizeof(buf), "XXX %s", mem->name); + } else if (mem->is_separator) { + snprintf(buf, sizeof(buf), " --- %s ---", mem->name); + } else if (mem->depsfailed == SOFT_FAILURE) { + snprintf(buf, sizeof(buf), "<%s> %s", mem->enabled ? "*" : " ", mem->name); + } else if (mem->conflictsfailed == SOFT_FAILURE) { + snprintf(buf, sizeof(buf), "(%s) %s", mem->enabled ? "*" : " ", mem->name); + } else { + snprintf(buf, sizeof(buf), "[%s] %s", mem->enabled ? "*" : " ", mem->name); + } + waddstr(menu, buf); + + if (curopt + 1 == i) + display_mem_info(menu, mem, start, end); + + if (i == end - (flags & SCROLL_DOWN ? 1 : 0)) + break; + } + + if (flags & SCROLL_DOWN) { + wmove(menu, j, max_x / 2 - sizeof(SCROLL_DOWN_INDICATOR) / 2); + waddstr(menu, SCROLL_DOWN_INDICATOR); + } + + wmove(menu, curopt - start, max_x / 2 - 9); + wrefresh(menu); +} + +static void play_space(void); + +static int move_up(int *current, int itemcount, int delta, int *start, int *end, int scroll) +{ + if (*current > 0) { + *current = MAX(*current - delta, 0); + if (*current < *start) { + int diff = *start - MAX(*start - delta, 0); + *start -= diff; + *end -= diff; + return 1; + } + } + return 0; +} + +static int move_down(int *current, int itemcount, int delta, int *start, int *end, int scroll) +{ + if (*current < itemcount) { + *current = MIN(*current + delta, itemcount); + if (*current > *end - 1 - (scroll & SCROLL_DOWN ? 1 : 0)) { + int diff = MIN(*end + delta - 1, itemcount) - *end + 1; + *start += diff; + *end += diff; + return 1; + } + } + return 0; +} + +static int run_category_menu(WINDOW *menu, int cat_num) +{ + struct category *cat; + int i = 0; + int start = 0; + int end = max_y - TITLE_HEIGHT - 8; + int c; + int curopt = 0; + int maxopt; + int changed = 1; + int scroll = SCROLL_NONE; + + AST_LIST_TRAVERSE(&categories, cat, list) { + if (i++ == cat_num) + break; + } + if (!cat) + return -1; + + maxopt = count_members(cat) - 1; + + if (maxopt > end) { + scroll = SCROLL_DOWN; + } + + draw_category_menu(menu, cat, start, end, curopt, changed, scroll); + + while ((c = getch())) { + changed = 0; + switch (c) { + case KEY_UP: + changed = move_up(&curopt, maxopt, 1, &start, &end, scroll); + break; + case KEY_DOWN: + changed = move_down(&curopt, maxopt, 1, &start, &end, scroll); + break; + case KEY_PPAGE: + changed = move_up( + &curopt, + maxopt, + MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)), + &start, + &end, + scroll); + break; + case KEY_NPAGE: + changed = move_down( + &curopt, + maxopt, + MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)), + &start, + &end, + scroll); + break; + case KEY_HOME: + changed = move_up(&curopt, maxopt, curopt, &start, &end, scroll); + break; + case KEY_END: + changed = move_down(&curopt, maxopt, maxopt - curopt, &start, &end, scroll); + break; + case KEY_LEFT: + case 27: /* Esc key */ + return 0; + case KEY_RIGHT: + case KEY_ENTER: + case '\n': + case ' ': + toggle_enabled_index(cat, curopt); + changed = 1; + break; + case 'y': + case 'Y': + set_enabled(cat, curopt); + changed = 1; + break; + case 'n': + case 'N': + clear_enabled(cat, curopt); + changed = 1; + break; + case 'h': + case 'H': + show_help(menu); + changed = 1; + break; + case KEY_F(7): + set_all(cat, 0); + changed = 1; + break; + case KEY_F(8): + set_all(cat, 1); + changed = 1; + default: + break; + } + if (c == 'x' || c == 'X' || c == 'Q' || c == 'q') + break; + + if (end <= maxopt) { + scroll |= SCROLL_DOWN; + } else { + scroll &= ~SCROLL_DOWN; + } + + draw_category_menu(menu, cat, start, end, curopt, changed, scroll); + } + + wrefresh(menu); + + return c; +} + +static void draw_title_window(WINDOW *title) +{ + char titlebar[strlen(menu_name) + 9]; + + memset(titlebar, '*', sizeof(titlebar) - 1); + titlebar[sizeof(titlebar) - 1] = '\0'; + wclear(title); + wmove(title, 1, (max_x / 2) - (strlen(titlebar) / 2)); + waddstr(title, titlebar); + wmove(title, 2, (max_x / 2) - (strlen(menu_name) / 2)); + waddstr(title, (char *) menu_name); + wmove(title, 3, (max_x / 2) - (strlen(titlebar) / 2)); + waddstr(title, titlebar); + wmove(title, 5, (max_x / 2) - (strlen(MENU_HELP) / 2)); + waddstr(title, MENU_HELP); + wrefresh(title); +} + +int run_menu(void) +{ + WINDOW *title; + WINDOW *menu; + int maxopt; + int curopt = 0; + int c; + int res = 0; + + setenv("ESCDELAY", "0", 1); /* So that ESC is processed immediately */ + + initscr(); + getmaxyx(stdscr, max_y, max_x); + sigaction(SIGWINCH, &winch_handler, NULL); /* handle window resizing in xterm */ + sigaction(SIGINT, &sigint_handler, NULL); /* handle window resizing in xterm */ + + if (max_x < MIN_X || max_y < MIN_Y) { + fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y); + endwin(); + return -1; + } + + cbreak(); /* don't buffer input until the enter key is pressed */ + noecho(); /* don't echo user input to the screen */ + keypad(stdscr, TRUE); /* allow the use of arrow keys */ + clear(); + refresh(); + + maxopt = count_categories() - 1; + + /* We have two windows - the title window at the top, and the menu window gets the rest */ + title = newwin(TITLE_HEIGHT, max_x, 0, 0); + menu = newwin(max_y - TITLE_HEIGHT, max_x, TITLE_HEIGHT, 0); + draw_title_window(title); + draw_main_menu(menu, curopt); + + while ((c = getch())) { + switch (c) { + case KEY_UP: + if (curopt > 0) + curopt--; + break; + case KEY_DOWN: + if (curopt < maxopt) + curopt++; + break; + case KEY_HOME: + curopt = 0; + break; + case KEY_END: + curopt = maxopt; + break; + case KEY_RIGHT: + case KEY_ENTER: + case '\n': + case ' ': + c = run_category_menu(menu, curopt); + break; + case 'h': + case 'H': + show_help(menu); + break; + case 'i': + case 'I': + play_space(); + draw_title_window(title); + default: + break; + } + if (c == 'q' || c == 'Q' || c == 27 || c == 3) { + if (changes_made) { + c = really_quit(menu); + if (c == 'q') { + res = -1; + break; + } + } else { + res = -1; + break; + } + } + if (c == 'x' || c == 'X' || c == 's' || c == 'S') + break; + draw_main_menu(menu, curopt); + } + + endwin(); + + return res; +} + +enum blip_type { + BLIP_TANK = 0, + BLIP_SHOT, + BLIP_BOMB, + BLIP_ALIEN, + BLIP_BARRIER, + BLIP_UFO +}; + +struct blip { + enum blip_type type; + int x; + int y; + int ox; + int oy; + int goingleft; + int health; + AST_LIST_ENTRY(blip) entry; +}; + +static AST_LIST_HEAD_NOLOCK(, blip) blips; + +static int respawn = 0; +static int score = 0; +static int num_aliens = 0; +static int alien_sleeptime = 0; +struct blip *ufo = NULL; +struct blip *tank = NULL; + +/*! Probability of a bomb, out of 100 */ +#define BOMB_PROB 1 + +static int add_barrier(int x, int y) +{ + struct blip *cur = NULL; + + cur = calloc(1,sizeof(struct blip)); + if(!cur) { + return -1; + } + cur->type=BLIP_BARRIER; + cur->x = x; + cur->y=max_y - y; + cur->health = 1; + AST_LIST_INSERT_HEAD(&blips, cur,entry); + return 0; +} + +static int init_blips(void) +{ + int i, j; + struct blip *cur; + int offset = 4; + + srandom(time(NULL) + getpid()); + + /* make tank */ + cur = calloc(1, sizeof(struct blip)); + if (!cur) + return -1; + cur->type = BLIP_TANK; + cur->x = max_x / 2; + cur->y = max_y - 1; + AST_LIST_INSERT_HEAD(&blips, cur, entry); + tank = cur; + + /* 3 rows of 10 aliens */ + num_aliens = 0; + for (i = 0; i < 3; i++) { + for (j = 0; j < 10; j++) { + cur = calloc(1, sizeof(struct blip)); + if (!cur) + return -1; + cur->type = BLIP_ALIEN; + cur->x = (j * 2) + 1; + cur->y = (i * 2) + 2; + AST_LIST_INSERT_HEAD(&blips, cur, entry); + num_aliens++; + } + } + for(i=0; i < 4; i++) { + if (i > 0) + offset += 5 + ((max_x) -28) / 3; + add_barrier(offset + 1, 6); + add_barrier(offset + 2, 6); + add_barrier(offset + 3, 6); + + add_barrier(offset, 5); + add_barrier(offset + 1, 5); + add_barrier(offset + 2, 5); + add_barrier(offset + 3, 5); + add_barrier(offset + 4, 5); + + add_barrier(offset, 4); + add_barrier(offset + 1, 4); + add_barrier(offset + 3, 4); + add_barrier(offset + 4, 4); + } + return 0; +} + +static inline chtype type2chtype(enum blip_type type) +{ + switch (type) { + case BLIP_TANK: + return 'A'; + case BLIP_ALIEN: + return 'X'; + case BLIP_SHOT: + return '|'; + case BLIP_BOMB: + return 'o'; + case BLIP_BARRIER: + return '*'; + case BLIP_UFO: + return '@'; + default: + break; + } + return '?'; +} + +static int repaint_screen(void) +{ + struct blip *cur; + + wmove(stdscr, 0, 0); + wprintw(stdscr, "Score: %d", score); + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if (cur->x != cur->ox || cur->y != cur->oy) { + wmove(stdscr, cur->oy, cur->ox); + waddch(stdscr, ' '); + wmove(stdscr, cur->y, cur->x); + waddch(stdscr, type2chtype(cur->type)); + cur->ox = cur->x; + cur->oy = cur->y; + } + } + + wmove(stdscr, 0, max_x - 1); + + wrefresh(stdscr); + + return 0; +} + +static int tank_move_left(void) +{ + if (tank->x > 0) + tank->x--; + + return 0; +} + +static int tank_move_right(void) +{ + if (tank->x < (max_x - 1)) + tank->x++; + + return 0; +} + +static int count_shots(void) +{ + struct blip *cur; + int count = 0; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if (cur->type == BLIP_SHOT) + count++; + } + + return count; +} + +static int tank_shoot(void) +{ + struct blip *shot; + + if (count_shots() == 3) + return 0; + + score--; + + shot = calloc(1, sizeof(struct blip)); + if (!shot) + return -1; + shot->type = BLIP_SHOT; + shot->x = tank->x; + shot->y = max_y - 2; + AST_LIST_INSERT_HEAD(&blips, shot, entry); + + return 0; +} + +static int remove_blip(struct blip *blip) +{ + if (!blip) { + return -1; + } + + AST_LIST_REMOVE(&blips, blip, entry); + + if (blip->type == BLIP_ALIEN) { + num_aliens--; + } + wmove(stdscr, blip->oy, blip->ox); + waddch(stdscr, ' '); + free(blip); + + return 0; +} + +static int move_aliens(void) +{ + struct blip *cur; + struct blip *current_barrier; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if (cur->type != BLIP_ALIEN) { + /* do nothing if it's not an alien */ + continue; + } + if (cur->goingleft && (cur->x == 0)) { + cur->y++; + cur->goingleft = 0; + } else if (!cur->goingleft && cur->x == (max_x - 1)) { + cur->y++; + cur->goingleft = 1; + } else if (cur->goingleft) { + cur->x--; + } else { + cur->x++; + } + /* Alien into the tank == game over */ + if (cur->x == tank->x && cur->y == tank->y) + return 1; + AST_LIST_TRAVERSE(&blips, current_barrier, entry){ + if(current_barrier->type!=BLIP_BARRIER) + continue; + if(cur->y == current_barrier->y && cur->x == current_barrier -> x) + remove_blip(current_barrier); + } + if (random() % 100 < BOMB_PROB && cur->y != max_y) { + struct blip *bomb = calloc(1, sizeof(struct blip)); + if (!bomb) + continue; + bomb->type = BLIP_BOMB; + bomb->x = cur->x; + bomb->y = cur->y + 1; + AST_LIST_INSERT_HEAD(&blips, bomb, entry); + } + } + + return 0; +} + +static int move_bombs(void) +{ + struct blip *cur; + struct blip *current_barrier; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + int mark = 0; + if (cur->type != BLIP_BOMB) + continue; + cur->y++; + if (cur->x == tank->x && cur->y == tank->y) { + return 1; + } + + AST_LIST_TRAVERSE(&blips, current_barrier, entry) { + if (current_barrier->type != BLIP_BARRIER) + continue; + if (cur->x == current_barrier->x && cur->y == current_barrier->y) { + mark = 1; + current_barrier->health--; + if (current_barrier->health == 0) + remove_blip(current_barrier); + } + } + if (mark){ + remove_blip(cur);} + } + + return 0; +} + +static void move_shots(void) +{ + struct blip *cur; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if (cur->type != BLIP_SHOT) + continue; + cur->y--; + } +} + + +static int ufo_action() +{ + struct blip *cur; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if (cur->type != BLIP_UFO) { + continue; + } + + cur->x--; + + if (cur->x < 0) { + remove_blip(cur); + respawn += 1; + } + + } + + if (respawn == 7) { + respawn = 0; + /* make new mothership*/ + cur = calloc(1, sizeof(struct blip)); + if(!cur) + return -1; + cur->type = BLIP_UFO; + cur->x = max_x - 1; + cur->y = 1; + AST_LIST_INSERT_HEAD(&blips, cur, entry); + } + + return 0; +} + +static void game_over(int win) +{ + clear(); + + wmove(stdscr, max_y / 2, max_x / 2 - 10); + wprintw(stdscr, "Game over! You %s!", win ? "win" : "lose"); + + wmove(stdscr, 0, max_x - 1); + + wrefresh(stdscr); + + sleep(1); + + while (getch() != ' '); + + return; +} + +static int check_shot(struct blip *shot) +{ + struct blip *cur; + + AST_LIST_TRAVERSE(&blips, cur, entry) { + if ((cur->type == BLIP_ALIEN || cur->type == BLIP_UFO) && cur->x == shot->x && cur->y == shot->y){ + if (cur->type == BLIP_UFO) { + score += 80; + } + score += 20; + remove_blip(cur); + remove_blip(shot); + respawn += 1; + if (!num_aliens) { + if(alien_sleeptime < 101) { + game_over(1); + return 1; + } else { + alien_sleeptime = alien_sleeptime - 100; + return 1; + } + } + break; + } + if (cur->type == BLIP_BARRIER) { + if (shot->x == cur->x && shot->y == cur->y) { + remove_blip(cur); + remove_blip(shot); + break; + } + } + } + + return 0; +} + +static int check_placement(void) +{ + struct blip *cur; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&blips, cur, entry) { + if (cur->y <= 0 || cur->y >= max_y) { + AST_LIST_REMOVE_CURRENT(&blips, entry); + remove_blip(cur); + } else if (cur->type == BLIP_SHOT && check_shot(cur)) + return 1; + } + AST_LIST_TRAVERSE_SAFE_END + + return 0; +} + +static void play_space(void) +{ + int c; + unsigned int jiffies = 1; + int quit = 0; + struct blip *blip; + alien_sleeptime = 1000; + score = 0; + + while(alien_sleeptime > 100) { + + jiffies = 1; + clear(); + nodelay(stdscr, TRUE); + init_blips(); + repaint_screen(); + + for (;;) { + c = getch(); + switch (c) { + case ' ': + tank_shoot(); + break; + case KEY_LEFT: + tank_move_left(); + break; + case KEY_RIGHT: + tank_move_right(); + break; + case 'x': + case 'X': + case 'q': + case 'Q': + quit = 1; + default: + /* ignore unknown input */ + break; + } + if (quit) { + alien_sleeptime = 1; + break; + } + if (!(jiffies % 25)) { + if (move_aliens() || move_bombs() || ufo_action()) { + alien_sleeptime = 1; + game_over(0); + break; + } + if (check_placement()) + break; + } + if (!(jiffies % 10)) { + move_shots(); + if (check_placement()) + break; + } + repaint_screen(); + jiffies++; + usleep(alien_sleeptime); + } + + while ((blip = AST_LIST_REMOVE_HEAD(&blips, entry))) + free(blip); + } + + nodelay(stdscr, FALSE); +} |