menuselect_curses.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2005 - 2006, Russell Bryant
  5. *
  6. * Russell Bryant <russell@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*
  19. * \file
  20. *
  21. * \author Russell Bryant <russell@digium.com>
  22. *
  23. * \brief curses frontend for selection maintenance
  24. */
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <unistd.h>
  29. #include <string.h>
  30. #include <signal.h>
  31. #include <time.h>
  32. #include <curses.h>
  33. #include "menuselect.h"
  34. #define MENU_HELP "Press 'h' for help."
  35. #define TITLE_HEIGHT 7
  36. #define MIN_X 80
  37. #define MIN_Y 27
  38. #define PAGE_OFFSET 10
  39. #define SCROLL_NONE 0
  40. #define SCROLL_DOWN 1
  41. #define SCROLL_DOWN_INDICATOR "... More ..."
  42. #define MIN(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a > __b) ? __b : __a);})
  43. #define MAX(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); ((__a < __b) ? __b : __a);})
  44. extern int changes_made;
  45. /*! Maximum number of characters horizontally */
  46. static int max_x = 0;
  47. /*! Maximum number of characters vertically */
  48. static int max_y = 0;
  49. static const char * const help_info[] = {
  50. "scroll => up/down arrows",
  51. "toggle selection => Enter",
  52. "select => y",
  53. "deselect => n",
  54. "select all => F8",
  55. "deselect all => F7",
  56. "back => left arrow",
  57. "quit => q",
  58. "save and quit => x",
  59. "",
  60. "XXX means dependencies have not been met",
  61. " or a conflict exists",
  62. "",
  63. "< > means a dependency has been deselected",
  64. " and will be automatically re-selected",
  65. " if this item is selected",
  66. "",
  67. "( ) means a conflicting item has been",
  68. " selected",
  69. };
  70. /*! \brief Handle a window resize in xterm */
  71. static void _winch_handler(int sig)
  72. {
  73. getmaxyx(stdscr, max_y, max_x);
  74. if (max_x < MIN_X || max_y < MIN_Y) {
  75. fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y);
  76. max_x = MIN_X - 1;
  77. max_y = MIN_Y - 1;
  78. }
  79. }
  80. static struct sigaction winch_handler = {
  81. .sa_handler = _winch_handler,
  82. };
  83. /*! \brief Handle a SIGQUIT */
  84. static void _sigint_handler(int sig)
  85. {
  86. }
  87. static struct sigaction sigint_handler = {
  88. .sa_handler = _sigint_handler,
  89. };
  90. /*! \brief Display help information */
  91. static void show_help(WINDOW *win)
  92. {
  93. int i;
  94. wclear(win);
  95. for (i = 0; i < (sizeof(help_info) / sizeof(help_info[0])); i++) {
  96. wmove(win, i, max_x / 2 - 15);
  97. waddstr(win, (char *) help_info[i]);
  98. }
  99. wrefresh(win);
  100. getch(); /* display the help until the user hits a key */
  101. }
  102. static int really_quit(WINDOW *win)
  103. {
  104. int c;
  105. wclear(win);
  106. wmove(win, 2, max_x / 2 - 15);
  107. waddstr(win, "ARE YOU SURE?");
  108. wmove(win, 3, max_x / 2 - 12);
  109. waddstr(win, "--- It appears you have made some changes, and");
  110. wmove(win, 4, max_x / 2 - 12);
  111. waddstr(win, "you have opted to Quit without saving these changes!");
  112. wmove(win, 6, max_x / 2 - 12);
  113. waddstr(win, " Please Enter Y to exit without saving;");
  114. wmove(win, 7, max_x / 2 - 12);
  115. waddstr(win, " Enter N to cancel your decision to quit,");
  116. wmove(win, 8, max_x / 2 - 12);
  117. waddstr(win, " and keep working in menuselect, or");
  118. wmove(win, 9, max_x / 2 - 12);
  119. waddstr(win, " Enter S to save your changes, and exit");
  120. wmove(win, 10, max_x / 2 - 12);
  121. wrefresh(win);
  122. while ((c=getch())) {
  123. if (c == 'Y' || c == 'y') {
  124. c = 'q';
  125. break;
  126. }
  127. if (c == 'S' || c == 's') {
  128. c = 'S';
  129. break;
  130. }
  131. if (c == 'N' || c == 'n') {
  132. c = '%';
  133. break;
  134. }
  135. }
  136. return c;
  137. }
  138. static void draw_main_menu(WINDOW *menu, int curopt)
  139. {
  140. struct category *cat;
  141. char buf[64];
  142. int i = 0;
  143. wclear(menu);
  144. AST_LIST_TRAVERSE(&categories, cat, list) {
  145. wmove(menu, i++, max_x / 2 - 10);
  146. snprintf(buf, sizeof(buf), " %s", strlen_zero(cat->displayname) ? cat->name : cat->displayname);
  147. waddstr(menu, buf);
  148. }
  149. wmove(menu, curopt, (max_x / 2) - 15);
  150. waddstr(menu, "--->");
  151. wmove(menu, 0, 0);
  152. wrefresh(menu);
  153. }
  154. static void display_mem_info(WINDOW *menu, struct member *mem, int start, int end)
  155. {
  156. char buf[64];
  157. struct reference *dep;
  158. struct reference *con;
  159. struct reference *use;
  160. wmove(menu, end - start + 2, max_x / 2 - 16);
  161. wclrtoeol(menu);
  162. wmove(menu, end - start + 3, max_x / 2 - 16);
  163. wclrtoeol(menu);
  164. wmove(menu, end - start + 4, max_x / 2 - 16);
  165. wclrtoeol(menu);
  166. wmove(menu, end - start + 5, max_x / 2 - 16);
  167. wclrtoeol(menu);
  168. wmove(menu, end - start + 6, max_x / 2 - 16);
  169. wclrtoeol(menu);
  170. if (mem->displayname) {
  171. wmove(menu, end - start + 2, max_x / 2 - 16);
  172. waddstr(menu, (char *) mem->displayname);
  173. }
  174. if (!AST_LIST_EMPTY(&mem->deps)) {
  175. wmove(menu, end - start + 3, max_x / 2 - 16);
  176. strcpy(buf, "Depends on: ");
  177. AST_LIST_TRAVERSE(&mem->deps, dep, list) {
  178. strncat(buf, dep->displayname, sizeof(buf) - strlen(buf) - 1);
  179. strncat(buf, dep->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
  180. if (AST_LIST_NEXT(dep, list))
  181. strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
  182. }
  183. waddstr(menu, buf);
  184. }
  185. if (!AST_LIST_EMPTY(&mem->uses)) {
  186. wmove(menu, end - start + 4, max_x / 2 - 16);
  187. strcpy(buf, "Can use: ");
  188. AST_LIST_TRAVERSE(&mem->uses, use, list) {
  189. strncat(buf, use->displayname, sizeof(buf) - strlen(buf) - 1);
  190. strncat(buf, use->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
  191. if (AST_LIST_NEXT(use, list))
  192. strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
  193. }
  194. waddstr(menu, buf);
  195. }
  196. if (!AST_LIST_EMPTY(&mem->conflicts)) {
  197. wmove(menu, end - start + 5, max_x / 2 - 16);
  198. strcpy(buf, "Conflicts with: ");
  199. AST_LIST_TRAVERSE(&mem->conflicts, con, list) {
  200. strncat(buf, con->displayname, sizeof(buf) - strlen(buf) - 1);
  201. strncat(buf, con->member ? "(M)" : "(E)", sizeof(buf) - strlen(buf) - 1);
  202. if (AST_LIST_NEXT(con, list))
  203. strncat(buf, ", ", sizeof(buf) - strlen(buf) - 1);
  204. }
  205. waddstr(menu, buf);
  206. }
  207. if (!mem->is_separator) { /* Separators lack support levels */
  208. { /* support level */
  209. wmove(menu, end - start + 6, max_x / 2 - 16);
  210. snprintf(buf, sizeof(buf), "Support Level: %s", mem->support_level);
  211. if (mem->replacement && *mem->replacement) {
  212. char buf2[64];
  213. snprintf(buf2, sizeof(buf2), ", Replaced by: %s", mem->replacement);
  214. strncat(buf, buf2, sizeof(buf) - strlen(buf) - 1);
  215. }
  216. waddstr(menu, buf);
  217. }
  218. }
  219. }
  220. static void draw_category_menu(WINDOW *menu, struct category *cat, int start, int end, int curopt, int changed, int flags)
  221. {
  222. int i = 0;
  223. int j = 0;
  224. struct member *mem;
  225. char buf[64];
  226. if (!changed) {
  227. /* If all we have to do is move the cursor,
  228. * then don't clear the screen and start over */
  229. AST_LIST_TRAVERSE(&cat->members, mem, list) {
  230. i++;
  231. if (curopt + 1 == i) {
  232. display_mem_info(menu, mem, start, end);
  233. break;
  234. }
  235. }
  236. wmove(menu, curopt - start, max_x / 2 - 9);
  237. wrefresh(menu);
  238. return;
  239. }
  240. wclear(menu);
  241. i = 0;
  242. AST_LIST_TRAVERSE(&cat->members, mem, list) {
  243. if (i < start) {
  244. i++;
  245. continue;
  246. }
  247. wmove(menu, j++, max_x / 2 - 10);
  248. i++;
  249. if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE)) {
  250. snprintf(buf, sizeof(buf), "XXX %s", mem->name);
  251. } else if (mem->is_separator) {
  252. snprintf(buf, sizeof(buf), " --- %s ---", mem->name);
  253. } else if (mem->depsfailed == SOFT_FAILURE) {
  254. snprintf(buf, sizeof(buf), "<%s> %s", mem->enabled ? "*" : " ", mem->name);
  255. } else if (mem->conflictsfailed == SOFT_FAILURE) {
  256. snprintf(buf, sizeof(buf), "(%s) %s", mem->enabled ? "*" : " ", mem->name);
  257. } else {
  258. snprintf(buf, sizeof(buf), "[%s] %s", mem->enabled ? "*" : " ", mem->name);
  259. }
  260. waddstr(menu, buf);
  261. if (curopt + 1 == i)
  262. display_mem_info(menu, mem, start, end);
  263. if (i == end - (flags & SCROLL_DOWN ? 1 : 0))
  264. break;
  265. }
  266. if (flags & SCROLL_DOWN) {
  267. wmove(menu, j, max_x / 2 - sizeof(SCROLL_DOWN_INDICATOR) / 2);
  268. waddstr(menu, SCROLL_DOWN_INDICATOR);
  269. }
  270. wmove(menu, curopt - start, max_x / 2 - 9);
  271. wrefresh(menu);
  272. }
  273. static void play_space(void);
  274. static int move_up(int *current, int itemcount, int delta, int *start, int *end, int scroll)
  275. {
  276. if (*current > 0) {
  277. *current = MAX(*current - delta, 0);
  278. if (*current < *start) {
  279. int diff = *start - MAX(*start - delta, 0);
  280. *start -= diff;
  281. *end -= diff;
  282. return 1;
  283. }
  284. }
  285. return 0;
  286. }
  287. static int move_down(int *current, int itemcount, int delta, int *start, int *end, int scroll)
  288. {
  289. if (*current < itemcount) {
  290. *current = MIN(*current + delta, itemcount);
  291. if (*current > *end - 1 - (scroll & SCROLL_DOWN ? 1 : 0)) {
  292. int diff = MIN(*end + delta - 1, itemcount) - *end + 1;
  293. *start += diff;
  294. *end += diff;
  295. return 1;
  296. }
  297. }
  298. return 0;
  299. }
  300. static int run_category_menu(WINDOW *menu, int cat_num)
  301. {
  302. struct category *cat;
  303. int i = 0;
  304. int start = 0;
  305. int end = max_y - TITLE_HEIGHT - 8;
  306. int c;
  307. int curopt = 0;
  308. int maxopt;
  309. int changed = 1;
  310. int scroll = SCROLL_NONE;
  311. AST_LIST_TRAVERSE(&categories, cat, list) {
  312. if (i++ == cat_num)
  313. break;
  314. }
  315. if (!cat)
  316. return -1;
  317. maxopt = count_members(cat) - 1;
  318. if (maxopt > end) {
  319. scroll = SCROLL_DOWN;
  320. }
  321. draw_category_menu(menu, cat, start, end, curopt, changed, scroll);
  322. while ((c = getch())) {
  323. changed = 0;
  324. switch (c) {
  325. case KEY_UP:
  326. changed = move_up(&curopt, maxopt, 1, &start, &end, scroll);
  327. break;
  328. case KEY_DOWN:
  329. changed = move_down(&curopt, maxopt, 1, &start, &end, scroll);
  330. break;
  331. case KEY_PPAGE:
  332. changed = move_up(
  333. &curopt,
  334. maxopt,
  335. MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)),
  336. &start,
  337. &end,
  338. scroll);
  339. break;
  340. case KEY_NPAGE:
  341. changed = move_down(
  342. &curopt,
  343. maxopt,
  344. MIN(PAGE_OFFSET, max_y - TITLE_HEIGHT - 6 - (scroll & SCROLL_DOWN ? 1 : 0)),
  345. &start,
  346. &end,
  347. scroll);
  348. break;
  349. case KEY_HOME:
  350. changed = move_up(&curopt, maxopt, curopt, &start, &end, scroll);
  351. break;
  352. case KEY_END:
  353. changed = move_down(&curopt, maxopt, maxopt - curopt, &start, &end, scroll);
  354. break;
  355. case KEY_LEFT:
  356. case 27: /* Esc key */
  357. return 0;
  358. case KEY_RIGHT:
  359. case KEY_ENTER:
  360. case '\n':
  361. case ' ':
  362. toggle_enabled_index(cat, curopt);
  363. changed = 1;
  364. break;
  365. case 'y':
  366. case 'Y':
  367. set_enabled(cat, curopt);
  368. changed = 1;
  369. break;
  370. case 'n':
  371. case 'N':
  372. clear_enabled(cat, curopt);
  373. changed = 1;
  374. break;
  375. case 'h':
  376. case 'H':
  377. show_help(menu);
  378. changed = 1;
  379. break;
  380. case KEY_F(7):
  381. set_all(cat, 0);
  382. changed = 1;
  383. break;
  384. case KEY_F(8):
  385. set_all(cat, 1);
  386. changed = 1;
  387. default:
  388. break;
  389. }
  390. if (c == 'x' || c == 'X' || c == 'Q' || c == 'q')
  391. break;
  392. if (end <= maxopt) {
  393. scroll |= SCROLL_DOWN;
  394. } else {
  395. scroll &= ~SCROLL_DOWN;
  396. }
  397. draw_category_menu(menu, cat, start, end, curopt, changed, scroll);
  398. }
  399. wrefresh(menu);
  400. return c;
  401. }
  402. static void draw_title_window(WINDOW *title)
  403. {
  404. char titlebar[strlen(menu_name) + 9];
  405. memset(titlebar, '*', sizeof(titlebar) - 1);
  406. titlebar[sizeof(titlebar) - 1] = '\0';
  407. wclear(title);
  408. wmove(title, 1, (max_x / 2) - (strlen(titlebar) / 2));
  409. waddstr(title, titlebar);
  410. wmove(title, 2, (max_x / 2) - (strlen(menu_name) / 2));
  411. waddstr(title, (char *) menu_name);
  412. wmove(title, 3, (max_x / 2) - (strlen(titlebar) / 2));
  413. waddstr(title, titlebar);
  414. wmove(title, 5, (max_x / 2) - (strlen(MENU_HELP) / 2));
  415. waddstr(title, MENU_HELP);
  416. wrefresh(title);
  417. }
  418. int run_menu(void)
  419. {
  420. WINDOW *title;
  421. WINDOW *menu;
  422. int maxopt;
  423. int curopt = 0;
  424. int c;
  425. int res = 0;
  426. setenv("ESCDELAY", "0", 1); /* So that ESC is processed immediately */
  427. initscr();
  428. getmaxyx(stdscr, max_y, max_x);
  429. sigaction(SIGWINCH, &winch_handler, NULL); /* handle window resizing in xterm */
  430. sigaction(SIGINT, &sigint_handler, NULL); /* handle window resizing in xterm */
  431. if (max_x < MIN_X || max_y < MIN_Y) {
  432. fprintf(stderr, "Terminal must be at least %d x %d.\n", MIN_X, MIN_Y);
  433. endwin();
  434. return -1;
  435. }
  436. cbreak(); /* don't buffer input until the enter key is pressed */
  437. noecho(); /* don't echo user input to the screen */
  438. keypad(stdscr, TRUE); /* allow the use of arrow keys */
  439. clear();
  440. refresh();
  441. maxopt = count_categories() - 1;
  442. /* We have two windows - the title window at the top, and the menu window gets the rest */
  443. title = newwin(TITLE_HEIGHT, max_x, 0, 0);
  444. menu = newwin(max_y - TITLE_HEIGHT, max_x, TITLE_HEIGHT, 0);
  445. draw_title_window(title);
  446. draw_main_menu(menu, curopt);
  447. while ((c = getch())) {
  448. switch (c) {
  449. case KEY_UP:
  450. if (curopt > 0)
  451. curopt--;
  452. break;
  453. case KEY_DOWN:
  454. if (curopt < maxopt)
  455. curopt++;
  456. break;
  457. case KEY_HOME:
  458. curopt = 0;
  459. break;
  460. case KEY_END:
  461. curopt = maxopt;
  462. break;
  463. case KEY_RIGHT:
  464. case KEY_ENTER:
  465. case '\n':
  466. case ' ':
  467. c = run_category_menu(menu, curopt);
  468. break;
  469. case 'h':
  470. case 'H':
  471. show_help(menu);
  472. break;
  473. case 'i':
  474. case 'I':
  475. play_space();
  476. draw_title_window(title);
  477. default:
  478. break;
  479. }
  480. if (c == 'q' || c == 'Q' || c == 27 || c == 3) {
  481. if (changes_made) {
  482. c = really_quit(menu);
  483. if (c == 'q') {
  484. res = -1;
  485. break;
  486. }
  487. } else {
  488. res = -1;
  489. break;
  490. }
  491. }
  492. if (c == 'x' || c == 'X' || c == 's' || c == 'S')
  493. break;
  494. draw_main_menu(menu, curopt);
  495. }
  496. endwin();
  497. return res;
  498. }
  499. enum blip_type {
  500. BLIP_TANK = 0,
  501. BLIP_SHOT,
  502. BLIP_BOMB,
  503. BLIP_ALIEN,
  504. BLIP_BARRIER,
  505. BLIP_UFO
  506. };
  507. struct blip {
  508. enum blip_type type;
  509. int x;
  510. int y;
  511. int ox;
  512. int oy;
  513. int goingleft;
  514. int health;
  515. AST_LIST_ENTRY(blip) entry;
  516. };
  517. static AST_LIST_HEAD_NOLOCK(, blip) blips;
  518. static int respawn = 0;
  519. static int score = 0;
  520. static int num_aliens = 0;
  521. static int alien_sleeptime = 0;
  522. struct blip *ufo = NULL;
  523. struct blip *tank = NULL;
  524. /*! Probability of a bomb, out of 100 */
  525. #define BOMB_PROB 1
  526. static int add_barrier(int x, int y)
  527. {
  528. struct blip *cur = NULL;
  529. cur = calloc(1,sizeof(struct blip));
  530. if(!cur) {
  531. return -1;
  532. }
  533. cur->type=BLIP_BARRIER;
  534. cur->x = x;
  535. cur->y=max_y - y;
  536. cur->health = 1;
  537. AST_LIST_INSERT_HEAD(&blips, cur,entry);
  538. return 0;
  539. }
  540. static int init_blips(void)
  541. {
  542. int i, j;
  543. struct blip *cur;
  544. int offset = 4;
  545. srandom(time(NULL) + getpid());
  546. /* make tank */
  547. cur = calloc(1, sizeof(struct blip));
  548. if (!cur)
  549. return -1;
  550. cur->type = BLIP_TANK;
  551. cur->x = max_x / 2;
  552. cur->y = max_y - 1;
  553. AST_LIST_INSERT_HEAD(&blips, cur, entry);
  554. tank = cur;
  555. /* 3 rows of 10 aliens */
  556. num_aliens = 0;
  557. for (i = 0; i < 3; i++) {
  558. for (j = 0; j < 10; j++) {
  559. cur = calloc(1, sizeof(struct blip));
  560. if (!cur)
  561. return -1;
  562. cur->type = BLIP_ALIEN;
  563. cur->x = (j * 2) + 1;
  564. cur->y = (i * 2) + 2;
  565. AST_LIST_INSERT_HEAD(&blips, cur, entry);
  566. num_aliens++;
  567. }
  568. }
  569. for(i=0; i < 4; i++) {
  570. if (i > 0)
  571. offset += 5 + ((max_x) -28) / 3;
  572. add_barrier(offset + 1, 6);
  573. add_barrier(offset + 2, 6);
  574. add_barrier(offset + 3, 6);
  575. add_barrier(offset, 5);
  576. add_barrier(offset + 1, 5);
  577. add_barrier(offset + 2, 5);
  578. add_barrier(offset + 3, 5);
  579. add_barrier(offset + 4, 5);
  580. add_barrier(offset, 4);
  581. add_barrier(offset + 1, 4);
  582. add_barrier(offset + 3, 4);
  583. add_barrier(offset + 4, 4);
  584. }
  585. return 0;
  586. }
  587. static inline chtype type2chtype(enum blip_type type)
  588. {
  589. switch (type) {
  590. case BLIP_TANK:
  591. return 'A';
  592. case BLIP_ALIEN:
  593. return 'X';
  594. case BLIP_SHOT:
  595. return '|';
  596. case BLIP_BOMB:
  597. return 'o';
  598. case BLIP_BARRIER:
  599. return '*';
  600. case BLIP_UFO:
  601. return '@';
  602. default:
  603. break;
  604. }
  605. return '?';
  606. }
  607. static int repaint_screen(void)
  608. {
  609. struct blip *cur;
  610. wmove(stdscr, 0, 0);
  611. wprintw(stdscr, "Score: %d", score);
  612. AST_LIST_TRAVERSE(&blips, cur, entry) {
  613. if (cur->x != cur->ox || cur->y != cur->oy) {
  614. wmove(stdscr, cur->oy, cur->ox);
  615. waddch(stdscr, ' ');
  616. wmove(stdscr, cur->y, cur->x);
  617. waddch(stdscr, type2chtype(cur->type));
  618. cur->ox = cur->x;
  619. cur->oy = cur->y;
  620. }
  621. }
  622. wmove(stdscr, 0, max_x - 1);
  623. wrefresh(stdscr);
  624. return 0;
  625. }
  626. static int tank_move_left(void)
  627. {
  628. if (tank->x > 0)
  629. tank->x--;
  630. return 0;
  631. }
  632. static int tank_move_right(void)
  633. {
  634. if (tank->x < (max_x - 1))
  635. tank->x++;
  636. return 0;
  637. }
  638. static int count_shots(void)
  639. {
  640. struct blip *cur;
  641. int count = 0;
  642. AST_LIST_TRAVERSE(&blips, cur, entry) {
  643. if (cur->type == BLIP_SHOT)
  644. count++;
  645. }
  646. return count;
  647. }
  648. static int tank_shoot(void)
  649. {
  650. struct blip *shot;
  651. if (count_shots() == 3)
  652. return 0;
  653. score--;
  654. shot = calloc(1, sizeof(struct blip));
  655. if (!shot)
  656. return -1;
  657. shot->type = BLIP_SHOT;
  658. shot->x = tank->x;
  659. shot->y = max_y - 2;
  660. AST_LIST_INSERT_HEAD(&blips, shot, entry);
  661. return 0;
  662. }
  663. static int remove_blip(struct blip *blip)
  664. {
  665. if (!blip) {
  666. return -1;
  667. }
  668. AST_LIST_REMOVE(&blips, blip, entry);
  669. if (blip->type == BLIP_ALIEN) {
  670. num_aliens--;
  671. }
  672. wmove(stdscr, blip->oy, blip->ox);
  673. waddch(stdscr, ' ');
  674. free(blip);
  675. return 0;
  676. }
  677. static int move_aliens(void)
  678. {
  679. struct blip *cur;
  680. struct blip *current_barrier;
  681. AST_LIST_TRAVERSE(&blips, cur, entry) {
  682. if (cur->type != BLIP_ALIEN) {
  683. /* do nothing if it's not an alien */
  684. continue;
  685. }
  686. if (cur->goingleft && (cur->x == 0)) {
  687. cur->y++;
  688. cur->goingleft = 0;
  689. } else if (!cur->goingleft && cur->x == (max_x - 1)) {
  690. cur->y++;
  691. cur->goingleft = 1;
  692. } else if (cur->goingleft) {
  693. cur->x--;
  694. } else {
  695. cur->x++;
  696. }
  697. /* Alien into the tank == game over */
  698. if (cur->x == tank->x && cur->y == tank->y)
  699. return 1;
  700. AST_LIST_TRAVERSE(&blips, current_barrier, entry){
  701. if(current_barrier->type!=BLIP_BARRIER)
  702. continue;
  703. if(cur->y == current_barrier->y && cur->x == current_barrier -> x)
  704. remove_blip(current_barrier);
  705. }
  706. if (random() % 100 < BOMB_PROB && cur->y != max_y) {
  707. struct blip *bomb = calloc(1, sizeof(struct blip));
  708. if (!bomb)
  709. continue;
  710. bomb->type = BLIP_BOMB;
  711. bomb->x = cur->x;
  712. bomb->y = cur->y + 1;
  713. AST_LIST_INSERT_HEAD(&blips, bomb, entry);
  714. }
  715. }
  716. return 0;
  717. }
  718. static int move_bombs(void)
  719. {
  720. struct blip *cur;
  721. struct blip *current_barrier;
  722. AST_LIST_TRAVERSE(&blips, cur, entry) {
  723. int mark = 0;
  724. if (cur->type != BLIP_BOMB)
  725. continue;
  726. cur->y++;
  727. if (cur->x == tank->x && cur->y == tank->y) {
  728. return 1;
  729. }
  730. AST_LIST_TRAVERSE(&blips, current_barrier, entry) {
  731. if (current_barrier->type != BLIP_BARRIER)
  732. continue;
  733. if (cur->x == current_barrier->x && cur->y == current_barrier->y) {
  734. mark = 1;
  735. current_barrier->health--;
  736. if (current_barrier->health == 0)
  737. remove_blip(current_barrier);
  738. }
  739. }
  740. if (mark){
  741. remove_blip(cur);}
  742. }
  743. return 0;
  744. }
  745. static void move_shots(void)
  746. {
  747. struct blip *cur;
  748. AST_LIST_TRAVERSE(&blips, cur, entry) {
  749. if (cur->type != BLIP_SHOT)
  750. continue;
  751. cur->y--;
  752. }
  753. }
  754. static int ufo_action()
  755. {
  756. struct blip *cur;
  757. AST_LIST_TRAVERSE(&blips, cur, entry) {
  758. if (cur->type != BLIP_UFO) {
  759. continue;
  760. }
  761. cur->x--;
  762. if (cur->x < 0) {
  763. remove_blip(cur);
  764. respawn += 1;
  765. }
  766. }
  767. if (respawn == 7) {
  768. respawn = 0;
  769. /* make new mothership*/
  770. cur = calloc(1, sizeof(struct blip));
  771. if(!cur)
  772. return -1;
  773. cur->type = BLIP_UFO;
  774. cur->x = max_x - 1;
  775. cur->y = 1;
  776. AST_LIST_INSERT_HEAD(&blips, cur, entry);
  777. }
  778. return 0;
  779. }
  780. static void game_over(int win)
  781. {
  782. clear();
  783. wmove(stdscr, max_y / 2, max_x / 2 - 10);
  784. wprintw(stdscr, "Game over! You %s!", win ? "win" : "lose");
  785. wmove(stdscr, 0, max_x - 1);
  786. wrefresh(stdscr);
  787. sleep(1);
  788. while (getch() != ' ');
  789. return;
  790. }
  791. static int check_shot(struct blip *shot)
  792. {
  793. struct blip *cur;
  794. AST_LIST_TRAVERSE(&blips, cur, entry) {
  795. if ((cur->type == BLIP_ALIEN || cur->type == BLIP_UFO) && cur->x == shot->x && cur->y == shot->y){
  796. if (cur->type == BLIP_UFO) {
  797. score += 80;
  798. }
  799. score += 20;
  800. remove_blip(cur);
  801. remove_blip(shot);
  802. respawn += 1;
  803. if (!num_aliens) {
  804. if(alien_sleeptime < 101) {
  805. game_over(1);
  806. return 1;
  807. } else {
  808. alien_sleeptime = alien_sleeptime - 100;
  809. return 1;
  810. }
  811. }
  812. break;
  813. }
  814. if (cur->type == BLIP_BARRIER) {
  815. if (shot->x == cur->x && shot->y == cur->y) {
  816. remove_blip(cur);
  817. remove_blip(shot);
  818. break;
  819. }
  820. }
  821. }
  822. return 0;
  823. }
  824. static int check_placement(void)
  825. {
  826. struct blip *cur;
  827. AST_LIST_TRAVERSE_SAFE_BEGIN(&blips, cur, entry) {
  828. if (cur->y <= 0 || cur->y >= max_y) {
  829. AST_LIST_REMOVE_CURRENT(&blips, entry);
  830. remove_blip(cur);
  831. } else if (cur->type == BLIP_SHOT && check_shot(cur))
  832. return 1;
  833. }
  834. AST_LIST_TRAVERSE_SAFE_END
  835. return 0;
  836. }
  837. static void play_space(void)
  838. {
  839. int c;
  840. unsigned int jiffies = 1;
  841. int quit = 0;
  842. struct blip *blip;
  843. alien_sleeptime = 1000;
  844. score = 0;
  845. while(alien_sleeptime > 100) {
  846. jiffies = 1;
  847. clear();
  848. nodelay(stdscr, TRUE);
  849. init_blips();
  850. repaint_screen();
  851. for (;;) {
  852. c = getch();
  853. switch (c) {
  854. case ' ':
  855. tank_shoot();
  856. break;
  857. case KEY_LEFT:
  858. tank_move_left();
  859. break;
  860. case KEY_RIGHT:
  861. tank_move_right();
  862. break;
  863. case 'x':
  864. case 'X':
  865. case 'q':
  866. case 'Q':
  867. quit = 1;
  868. default:
  869. /* ignore unknown input */
  870. break;
  871. }
  872. if (quit) {
  873. alien_sleeptime = 1;
  874. break;
  875. }
  876. if (!(jiffies % 25)) {
  877. if (move_aliens() || move_bombs() || ufo_action()) {
  878. alien_sleeptime = 1;
  879. game_over(0);
  880. break;
  881. }
  882. if (check_placement())
  883. break;
  884. }
  885. if (!(jiffies % 10)) {
  886. move_shots();
  887. if (check_placement())
  888. break;
  889. }
  890. repaint_screen();
  891. jiffies++;
  892. usleep(alien_sleeptime);
  893. }
  894. while ((blip = AST_LIST_REMOVE_HEAD(&blips, entry)))
  895. free(blip);
  896. }
  897. nodelay(stdscr, FALSE);
  898. }