menuselect_curses.c 22 KB

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