invaders.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /* This file is part of Invaders.
  2. *
  3. * Copyright (C) 2020 LCM.
  4. * You may use, distribute and modify Invaders under the terms of the
  5. * GPLv3 license, available at <https://www.gnu.org/licenses/\>.
  6. */
  7. #include "bullet.hpp"
  8. #include "enemy.hpp"
  9. #include "player.hpp"
  10. #include "boss.hpp"
  11. #include "powerup.hpp"
  12. #include "definitions.hpp"
  13. #include "functions.hpp"
  14. #include "wall.hpp"
  15. using std::cout;
  16. using std::endl;
  17. using std::min;
  18. int main(int argc,char** argv)
  19. {
  20. bool sound = false;
  21. if(argc>1){
  22. if(strcmp(argv[1],"--uber-debug")!=0 && strcmp(argv[1],"--info")!=0 && strcmp(argv[1],"--autowin")!=0 &&
  23. strcmp(argv[1], "--mute")!=0 && strcmp(argv[1], "-m")!=0 && strcmp(argv[1], "--sound")!=0 && strcmp(argv[1], "-s")!=0){
  24. cout<<"Usage: "<<argv[0]<<" [--info|--mute|--sound]"<<endl;
  25. return 1;
  26. }
  27. if(strcmp(argv[1],"--info")==0){
  28. cout<<endl<<"Curses Invaders " VERSION <<endl;
  29. cout<<endl<<"Game developed by Giacomo Parolini (jp) and Enrico Guiraud (blue) in years 2010-2013."<<endl;
  30. cout<<"Source code is available under request to jp@lcm.mi.infn.it (it's quite ugly, I warn you ;-) )"<<endl;
  31. cout<<"Report any bug to the same mail address."<<endl<<endl;
  32. return 1;
  33. }
  34. if(strcmp(argv[1], "--mute")==0 || strcmp(argv[1], "-m")==0) {
  35. sound = false;
  36. } else if(strcmp(argv[1], "--sound")==0 || strcmp(argv[1], "-s")==0) {
  37. sound = true;
  38. }
  39. }
  40. typedef std::list<bullet> b_list;
  41. typedef std::list<enemy> e_list;
  42. typedef std::vector<wall> w_vec;
  43. typedef std::list<rocket> r_list;
  44. srand(time(NULL));
  45. //OBJECTS INIZIALIZATION
  46. player player1; //player is automatically created in [C/2][R]
  47. e_list enemies; //vector of enemies
  48. b_list bullets; //list of player's bullets
  49. b_list bombs; //list of enemies' bombs
  50. b_list powerups; //list of powerups
  51. r_list rockets; //list of rockets
  52. int refresh_time; //how long the program waits before refreshing the screen
  53. int chflag=0; //cheat flag: 0=normal, 1=cheats allowed, 2=special mode activated, 3=both special mode and cheats.
  54. boss boss1;
  55. WINDOW *Score,*BossHP;
  56. int commands[CMD_NUM];
  57. read_commands(commands); //0:left,1:right,2:up,3:down,4:pause,5:shoot1,6:shoot2,7:mute,8:quit
  58. //PARAMETERS/UTILITIES
  59. double shootrate; //probability of an enemy shooting a bomb
  60. double poweruprate; //probability of a powerup being dropped
  61. int enemy_num; //number of enemies in the current match
  62. int walls_num; //number of walls
  63. int command; //keyboard input
  64. int score=0; //score: gain +100 when an enemy is destroyed and +50 when a bomb is destroyed
  65. int level=1; //difficulty level
  66. //PTHREAD STUFF
  67. pthread_t thread[THREADS_NUM];
  68. //NCURSES STUFF
  69. initscr();
  70. curs_set(0);
  71. noecho();
  72. nodelay(NULL, true);
  73. cbreak();
  74. keypad(stdscr,TRUE);
  75. start_color();
  76. init_pair(0,COLOR_WHITE,COLOR_BLACK);
  77. init_pair(1,COLOR_GREEN,COLOR_BLACK); //PLAYER
  78. init_pair(2,COLOR_RED,COLOR_BLACK); //ENEMY
  79. init_pair(3,COLOR_YELLOW,COLOR_BLACK); //BULLETS-POWERUPS
  80. init_pair(4,COLOR_MAGENTA,COLOR_BLACK); //WALLS
  81. init_pair(5,COLOR_CYAN,COLOR_BLACK); //BOSS
  82. init_pair(6,COLOR_BLUE,COLOR_BLACK); //BOMBS
  83. init_pair(7,COLOR_RED,COLOR_RED);
  84. init_pair(8,COLOR_GREEN,COLOR_GREEN);
  85. init_pair(9,COLOR_BLACK,COLOR_GREEN);
  86. //////////PROGRAM START: creation of game objects and set of parameters
  87. level = choose_level(commands); // get desired level
  88. setup_level(level, shootrate, poweruprate, refresh_time, boss1, enemy_num, walls_num); // set game parameters and boss
  89. load_enemies(enemies,enemy_num);
  90. player1.set_commands(commands);
  91. erase();
  92. w_vec walls(walls_num);
  93. Score=newwin(3,10,R/3,C+3);
  94. BossHP=newwin(3,15,R/3-3,C+3);
  95. int i=0;
  96. for(w_vec::iterator it=walls.begin(); it!=walls.end(); ++it, ++i) //creating walls (in a quite symmetric pattern)
  97. it->create((i+1)*(C/(3*walls_num+1))+i*(2*C/(3*walls_num+1)),2*R/3,(int)min(6,2*C/(3*(int)(walls_num)+1)),2,3);
  98. /////////ENTERING MAIN LOOP
  99. if(sound)
  100. pthread_create(&thread[0],NULL,pmusic,NULL); //executing background music in parallel thread
  101. if(argc>1){
  102. if(strcmp(argv[1],"--uber-debug")==0){ player1.weaponclass=5; player1.weaponclassrkt=5; player1.rocketlauncher=true; player1.length=MAX_LENGTH; }
  103. else if(strcmp(argv[1],"--autowin")==0){ Victory(boss1.name,score,level,0); endwin(); return 1; }
  104. }
  105. while(1)
  106. {
  107. napms(refresh_time); //ncurses sleep function (ms)
  108. timeout(0);
  109. command = getch();
  110. if(command!=ERR) //READING INPUT
  111. command = tolower(command);
  112. if(command == commands[8]){ //q = exit game
  113. endwin();
  114. pkill_music(thread[0]);
  115. cout<<lightgreen<<"Game exited correctly."<<none<<endl;
  116. return 1;
  117. }
  118. if(command == commands[4])
  119. pause_game(chflag); //p = pause game
  120. //=====CHEATS================================================
  121. if(chflag==1 || chflag==3)
  122. get_cheat(command,player1,enemies,shootrate);
  123. if(chflag!=2 && chflag!=3)
  124. get_SpecialMode(command,chflag,boss1,enemies,bullets,bombs,rockets);
  125. //======END CHEATS===============================================
  126. if(command == commands[0] || command == KEY_LEFT || command == commands[1] || command == KEY_RIGHT ||
  127. ((chflag==1 || chflag==3) && (command == commands[2]|| command == KEY_UP || command == commands[3] || command == KEY_DOWN))){ //a or d = move player (if cheats are triggered, also w and s)
  128. //for(int i=player1.x;i<player1.x+player1.length;i++) //mvaddch(player1.y,i,' ');
  129. player1.next_pos(command);
  130. }
  131. if(command==commands[5] || command==commands[6]){ //spacebar = shoot!
  132. if(sound) pthread_create(&thread[4],NULL,pshoot_sound,NULL);
  133. player1.shoot(bullets);
  134. if(player1.rocketlauncher) player1.shootrkt(rockets);
  135. }
  136. if(command==commands[7]){ //mute/unmute
  137. if(sound) pkill_music(thread[0]);
  138. else pthread_create(&thread[0],NULL,pmusic,NULL); // background music
  139. sound=!sound;
  140. }
  141. if(enemyalive(enemies)) //if there's at least one enemy alive:
  142. {
  143. int n_enemies=0;
  144. e_list::const_iterator e_end = enemies.end();
  145. for(e_list::iterator it=enemies.begin(); it!=e_end; ++it)
  146. if(it->alive) n_enemies++; //counts alive enemies
  147. for(e_list::iterator it=enemies.begin(); it!=e_end; ++it)
  148. {
  149. //mvaddch(it->y,it->x,' ');
  150. it->next_pos(); //evaluate new positions
  151. if(it->alive && (double)rand()/RAND_MAX<(shootrate*enemy_num/n_enemies)){ //try a bomb-dropping
  152. if(sound) pthread_create(&thread[1],NULL,penshoot_sound,NULL); // enemy shoot sound
  153. it->shoot(bombs);
  154. }
  155. }
  156. }
  157. else boss1.alive = true; //if no enemy is alive, boss spawns
  158. if(boss1.alive) //if boss is alive, moves and shoots
  159. {
  160. for(int i=0;i<boss1.width;i++) //mvaddch(boss1.y,boss1.x+i,' ');
  161. for(int j=0;j<boss1.height;j++){
  162. //mvaddch(boss1.y+j,boss1.x,' ');
  163. //mvaddch(boss1.y+j,boss1.x+boss1.width-1,' ');
  164. }
  165. boss1.next_pos();
  166. if((double)rand()/RAND_MAX<shootrate*25.){ //boss's shootrate is 25 times the one of normal enemies
  167. if(sound) pthread_create(&thread[1],NULL,penshoot_sound,NULL); // enemy shoot sound
  168. boss1.shoot(bombs);
  169. }
  170. }
  171. if(!bullets.empty()) //evaluate new bullets' positions
  172. for(b_list::iterator it=bullets.begin(); it!=bullets.end(); ++it){
  173. //mvaddch(it->y,it->x,' ');
  174. it->next_pos();
  175. }
  176. bullets.unique(); //remove duplicates of bullets (i.e. player can't shoot enemies)
  177. if(!rockets.empty()) //evaluate new rockets' positions
  178. for(r_list::iterator it=rockets.begin(); it!=rockets.end(); ++it){
  179. //mvaddch(it->y,it->x,' ');
  180. it->next_pos();
  181. }
  182. rockets.unique();
  183. if(!bombs.empty()) //evaluate new bombs' positions
  184. for(b_list::iterator it=bombs.begin(); it!=bombs.end(); ++it){
  185. //mvaddch(it->y,it->x,' ');
  186. it->next_pos();
  187. }
  188. bombs.unique(); //remove duplicates of bombs
  189. if(!powerups.empty()) //new powerups' positions
  190. for(b_list::iterator it=powerups.begin(); it!=powerups.end(); ++it){
  191. //mvaddch(it->y,it->x,' ');
  192. it->next_pos();
  193. }
  194. powerups.unique(); //FIXME: THIS ONLY WORKS ON CONSECUTIVE ELEMENTS OF THE LIST!! we should at least sort powerups before calling unique()
  195. interactions(player1,bullets,bombs,enemies,walls,powerups,rockets,boss1,score,poweruprate);
  196. ///////ENDGAME CHECKS
  197. if(boss1.health<1 || gameover(player1,bombs))
  198. {
  199. if(sound){
  200. pkill_music(thread[0]);
  201. pthread_create(&thread[2],NULL,pwin_theme,NULL);
  202. }
  203. WINDOW *replay;
  204. if(boss1.health<1) // YOU WON
  205. {
  206. Victory(boss1.name,score,level,chflag);
  207. replay=newwin(3,25,26,20);
  208. } else // YOU LOST
  209. {
  210. Defeat(score);
  211. replay=newwin(3,25,17,20);
  212. }
  213. box(replay,ACS_VLINE,ACS_HLINE);
  214. switch(char choice = playagain(replay)) // playagain() returns 'y', 'n' or 'q'
  215. {
  216. case 'y':
  217. case 'n':
  218. delwin(replay);
  219. timeout(500);
  220. score=0;
  221. erase();
  222. refresh();
  223. if(choice=='n')
  224. level = choose_level(commands); // get desired level
  225. setup_level(level, shootrate, poweruprate, refresh_time, boss1, enemy_num, walls_num); // set game parameters and boss
  226. load_enemies(enemies,enemy_num);
  227. reset(player1, enemies, boss1, bullets, bombs, walls, walls_num, powerups, rockets, chflag); // reset box, player, enemy and deletes all bullets and bombs
  228. erase();
  229. refresh();
  230. continue; // Go to the beginning of the main loop
  231. case 'q':
  232. endwin();
  233. cout<<lightgreen<<"Game exited correctly."<<none<<endl;
  234. return 0;
  235. }
  236. }
  237. draw(player1,bullets,bombs,enemies,walls,powerups,rockets,boss1);
  238. write_score(Score,score);
  239. if(boss1.alive){
  240. write_bosshp(BossHP,boss1.health,boss1.healthmax,boss1.name);
  241. wnoutrefresh(BossHP);
  242. }
  243. wnoutrefresh(Score);
  244. refresh();
  245. }
  246. ////////END OF MAIN LOOP
  247. endwin();
  248. return -1; //program never really reaches here, but compiler is happy
  249. }