home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / grafik / sprites / demosrc / sm.c next >
Encoding:
C/C++ Source or Header  |  1991-02-25  |  31.0 KB  |  985 lines

  1. /**********************************************************************
  2. * sm.c
  3. *
  4. * StarMines - a sprite toolkit demonstration game.
  5. *
  6. * Compile & link with the following command line:
  7. *   tcc -ms sm.c smspr.c smscores.c stks.lib graphics.lib
  8. *
  9. * (The sprite map files must be in the "smp" subdirectory)
  10. **********************************************************************
  11.                     This file is part of
  12.  
  13.           STK -- The sprite toolkit -- version 1.1
  14.  
  15.               Copyright (C) Jari Karjala 1991
  16.  
  17. The sprite toolkit (STK) is a FreeWare toolkit for creating high
  18. resolution sprite graphics with PCompatible hardware. This toolkit
  19. is provided as is without any warranty or such thing. See the file
  20. COPYING for further information.
  21.  
  22. **********************************************************************
  23. **********************************************************************/
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29. #include <alloc.h>
  30. #include <dos.h>
  31. #include <graphics.h>
  32.  
  33. extern unsigned _stklen = 8192;
  34.  
  35. #include "stk.h"
  36.  
  37. #include "sm.h"
  38. #include "smspr.h"
  39. #include "smscores.h"
  40.  
  41. #define MAX_BULLET 20
  42.  
  43. int sprite_resolution = 8;
  44. ALIEN_TYPE aliens[MAX_ALIEN];
  45. EXPL_TYPE expls[MAX_EXPL];
  46. ANIM_SPRITE player = NULL;
  47. ANIM_SPRITE player_explosion;
  48. SCORE_ENTRY se;
  49.  
  50. int player_dying;       /** non-zero when player is exploding **/
  51. int player_dead;        /** non-zero when player has exploded **/
  52. int player_immune;      /** non-zero when player is invincible **/
  53.  
  54. int lives;              /** number of lives **/
  55. long int score;         /** current score **/
  56. int score_inc;          /** score increment **/
  57. int bonus;              /** bonus after wave **/
  58. int bullets_flying;     /** number of bullets in the air **/
  59.  
  60. int wave;               /** the current alien wave **/
  61. int max_aliens;         /** initial number of aliens in one wave **/
  62. int alien_count;        /** number of aliens alive currently **/
  63. int alien_timeout;      /** timeout before divide alien **/
  64. int alien_speed;        /** alien speed **/
  65.  
  66. int cheat_wave=0;       /** the starting wave when cheating **/
  67.  
  68. struct dir_struct {
  69.     int dx,dy;
  70. };
  71. /** Deltas for each possible player movement direction (clockwise order) **/
  72. struct dir_struct player_dirs[16] = {
  73.     2,0,  2,-1, 2,-2, 1,-2,   0,-2, -1,-2, -2,-2, -2, -1,
  74.    -2,0, -2,1, -2,2, -1,2,    0,2,   1,2,   2,2,   2,1
  75. };
  76. /** Deltas from ship coordinates to the missile initial position **/
  77. struct dir_struct missile_deltas[16] = {
  78.     17,10, 17,7, 15,4, 12,2,    8,1, 5,1, 2,3, 0,6,
  79.     -1,10, -1,13, 1,16, 4,18,   8,19, 11,19, 14,17, 16,14
  80. };
  81. /** Initial mine directions **/
  82. struct dir_struct mine_dir[4] = { 1,1, -1,-1, -1,1, 1,-1 };
  83.  
  84. /** Maximum player X and Y velocities **/
  85. #define MAX_DX 10
  86. #define MAX_DY 8
  87.  
  88. /** After this many waves shot effects change. If you increase this, **/
  89. /** some waves may become impossible to pass. See handle_advancement.**/
  90. #define HIT_DIVIDES_LIM 4
  91.  
  92.  
  93. #define HISCORE     "The StarMines Hall of Fame:"
  94. #define HISCORE2    "  Give your name, please:  "
  95. #define HISCORE3    "%-18.18s %06ld"
  96. #define HISCORE_X   ((BOX_LEF +(BOX_RIG -BOX_LEF -8*strlen(HISCORE))/2)&0xFF8)
  97. #define HISCORE_Y   (BOX_TOP+10)
  98.  
  99. #define HISCORES_X ((BOX_LEF+(BOX_RIG-BOX_LEF-25*8)/2)&0xFF8)
  100. #define HISCORES_Y (BOX_TOP+25)
  101.  
  102. #define STARMINES   "StarMines"
  103. #define STARMINES_Y (BOX_TOP+12)
  104.  
  105. #define COPYRIGHT   " Copyright 1991 J.Karjala"
  106. #define COPYRIGHT_Y (BOX_BOT-15)
  107.  
  108. #define GAME_OVER2   "Press SPACE to restart"
  109. #define GAME_OVER2_Y (BOX_BOT-20)
  110. #define GAME_OVER3   "or Esc to exit"
  111. #define GAME_OVER3_Y (BOX_BOT-10)
  112. #define GAME_OVER   "*** GAME OVER ***"
  113. #define GAME_OVER_Y (BOX_BOT-40)
  114.  
  115. #define CURRENT_WAVE    "    MINE WAVE %d    "
  116. #define NEXT_WAVE1      "+*+ BONUS %05d +*+"
  117. #define NEXT_WAVE2      "*+* BONUS %05d *+*"
  118. #define NEXT_WAVE_Y (BOX_BOT-40)
  119.  
  120. #define SCORE       "SCORE %06ld"
  121. #define SCORE_X     (BOX_LEF+24)
  122. #define SCORE_Y     (BOX_TOP+40)
  123. #define LIVES       "LIVES %d"
  124. #define LIVES_X     (BOX_RIG-7*8-24)
  125. #define LIVES_Y     (BOX_TOP+40)
  126.  
  127.  
  128. /**********************************************************************
  129. * Increment the score by i and display it
  130. **********************************************************************/
  131. void inc_score(int i)
  132. {
  133.     score += (long)i;
  134.     gr_dual_xy_printf(SCORE_X, SCORE_Y, SCORE, score);
  135. }
  136.  
  137. /**********************************************************************
  138. * Decrement lives
  139. **********************************************************************/
  140. void dec_lives(void)
  141. {
  142.     if (lives>0)
  143.         lives--;
  144.  
  145.     gr_dual_xy_printf(LIVES_X, LIVES_Y, LIVES, lives);
  146. }
  147.  
  148. /**********************************************************************
  149. * Show the normal middle box on both pages. Leave 0 page active.
  150. **********************************************************************/
  151. void show_normal_box(void)
  152. {
  153.     gr_setactivepage(1);
  154.     setfillstyle(EMPTY_FILL, 0);
  155.     bar(BOX_LEF+1, BOX_TOP+1,  BOX_RIG-1, BOX_BOT-1);
  156.     gr_setactivepage(0);
  157.     setfillstyle(EMPTY_FILL, 0);
  158.     bar(BOX_LEF+1, BOX_TOP+1,  BOX_RIG-1, BOX_BOT-1);
  159.  
  160.     gr_dual_center_printf(STARMINES_Y, STARMINES);
  161.     gr_dual_center_printf(COPYRIGHT_Y, COPYRIGHT);
  162.     gr_dual_xy_printf(LIVES_X, LIVES_Y, LIVES, lives);
  163.     gr_dual_xy_printf(SCORE_X, SCORE_Y, SCORE, score);
  164.     gr_dual_center_printf(NEXT_WAVE_Y, CURRENT_WAVE, wave+1);
  165. }
  166.  
  167. /**********************************************************************
  168. * Show the high scores on both pages. Leave 0 page active
  169. **********************************************************************/
  170. void show_hiscores(void)
  171. {
  172.     SCORE_ENTRY *sep;
  173.     int y;
  174.     
  175.     gr_setactivepage(1);
  176.     setfillstyle(EMPTY_FILL, 0);
  177.     bar(BOX_LEF+1, BOX_TOP+1,  BOX_RIG-1, BOX_BOT-1);
  178.     line(HISCORE_X,HISCORE_Y+9, HISCORE_X+strlen(HISCORE)*8,HISCORE_Y+9);
  179.     gr_setactivepage(0);
  180.     setfillstyle(EMPTY_FILL, 0);
  181.     bar(BOX_LEF+1, BOX_TOP+1,  BOX_RIG-1, BOX_BOT-1);
  182.     line(HISCORE_X,HISCORE_Y+9, HISCORE_X+strlen(HISCORE)*8,HISCORE_Y+9);
  183.  
  184.     gr_dual_center_printf(HISCORE_Y, HISCORE);
  185.     sep = smscores_get_first();
  186.     y = HISCORES_Y;
  187.     while (sep!=NULL) {
  188.         gr_dual_center_printf(y, HISCORE3, sep->name, sep->score);
  189.         y += 10;
  190.         sep = smscores_get_next();
  191.     }
  192. }
  193.  
  194. /**********************************************************************
  195. * Ask for the name of the high scorer on current active page.
  196. **********************************************************************/
  197. void ask_name(SCORE_ENTRY *sep, int place)
  198. {
  199.     show_hiscores();
  200.     gr_xy_printf(HISCORE_X, HISCORE_Y, HISCORE2);
  201.     gr_xy_printf(HISCORES_X, HISCORES_Y+10*place,
  202.                  HISCORE3, " ", sep->score);
  203.     moveto(HISCORES_X, HISCORES_Y+10*place);
  204.     gr_gets(sep->name, 18);
  205. }
  206.  
  207. /**********************************************************************
  208. * Set new limits for the given sprite at position x,y
  209. **********************************************************************/
  210. void set_limits(ANIM_SPRITE as, int x, int y)
  211. {
  212.     ANIM_SPR_INFO *asi;
  213.     int top, bot, lef, rig;
  214.  
  215.     asi = spr_anim_get_info(as);
  216.  
  217.     lef = 0; rig = gr_max_x;
  218.     top = 0; bot = gr_max_y;
  219.  
  220.     if (x>BOX_RIG) {
  221.         lef = BOX_RIG; rig = gr_max_x;
  222.     }
  223.     else if (x <= (BOX_LEF - asi->w)) {
  224.         lef = 0; rig = BOX_LEF;
  225.     }
  226.     else if (y>BOX_BOT) {
  227.         top = BOX_BOT; bot = gr_max_y;
  228.     }
  229.     else if (y <= (BOX_TOP - asi->h)) {
  230.         top = 0; bot = BOX_TOP;
  231.     }
  232.     else { /* inside the box */
  233.         if (x>BOX_RIG-15) {
  234.             lef = BOX_RIG; rig = gr_max_x; x = BOX_RIG+1;
  235.         }
  236.         else if (x <= (BOX_LEF+15)) {
  237.             lef = 0; rig = BOX_LEF; x = BOX_LEF; 
  238.         }
  239.         else if (y>BOX_BOT-15) {
  240.             top = BOX_BOT; bot = gr_max_y; y = BOX_BOT;
  241.         }
  242.         else if (y <= (BOX_TOP+15)) {
  243.             top = 0; bot = BOX_TOP; y = BOX_TOP;
  244.         }
  245.         else {  /** This is a BUG **/
  246.             setwritemode(1);
  247.             rectangle(lef,top,rig,bot);
  248.             rectangle(lef,top,rig,bot);
  249.             gr_dual_xy_printf(0,0,"(%d,%d, %d,%d) ", x,y,asi->w,asi->h);
  250.         }
  251.     }
  252.     spr_anim_set_limits(as, lef, top, rig, bot);
  253. }
  254.  
  255. /**********************************************************************
  256. * Change sprite limits so that the sprite will not enter the middle box
  257. **********************************************************************/
  258. WORD handle_x_limit(ANIM_SPRITE aspr, ANIM_SPR_INFO *asi)
  259. {
  260.     if (asi->x > 0 && asi->x < gr_max_x - asi->w
  261.         && (asi->y < BOX_TOP - asi->h || asi->y > BOX_BOT)) {
  262.         /** only change limits **/
  263.         set_limits(aspr, asi->x, asi->y);
  264.     }
  265.     else {  /** delete if bullet, otherwise bounce sprite from wall **/
  266.         if (asi->id==BULLET_ID)
  267.             return SPR_ANIM_FX_RET_DESTROY;
  268.         else
  269.         if (asi->id>=EXPLO_ID)
  270.             return SPR_ANIM_FX_RET_STOP;
  271.         else {
  272.             if (asi->x > asi->rig)
  273.                 spr_anim_set_location(aspr, asi->rig, asi->y);
  274.             else
  275.                 spr_anim_set_location(aspr, asi->lef, asi->y);
  276.             spr_anim_set_vector(aspr, -asi->dx, asi->dy);
  277.         }
  278.     }
  279.     return SPR_ANIM_FX_RET_RE_PUT;
  280. }
  281.  
  282. /**********************************************************************
  283. * Change sprite limits so that the sprite will not enter the middle box
  284. **********************************************************************/
  285. WORD handle_y_limit(ANIM_SPRITE aspr, ANIM_SPR_INFO *asi)
  286. {
  287.     if (asi->y > 0 && asi->y < gr_max_y - asi->h
  288.         && (asi->x < BOX_LEF - asi->w || asi->x > BOX_RIG)) {
  289.         /** only change limits **/
  290.         set_limits(aspr, asi->x, asi->y);
  291.     }
  292.     else {  /** delete if bullet, otherwise bounce sprite from wall **/
  293.         if (asi->id==BULLET_ID)
  294.             return SPR_ANIM_FX_RET_DESTROY;
  295.         else
  296.         if (asi->id>=EXPLO_ID)
  297.             return SPR_ANIM_FX_RET_STOP;
  298.         else {
  299.             if (asi->y > asi->bot)
  300.                 spr_anim_set_location(aspr, asi->x, asi->bot);
  301.             else
  302.                 spr_anim_set_location(aspr, asi->x, asi->top);
  303.             spr_anim_set_vector(aspr, asi->dx, -asi->dy);
  304.         }
  305.     }
  306.     return SPR_ANIM_FX_RET_RE_PUT;
  307. }
  308.  
  309. /**********************************************************************
  310. * Creates how_many aliens.
  311. **********************************************************************/
  312. void create_aliens(int how_many)
  313. {
  314.     int i,x,y;
  315.     ANIM_SPRITE alien;
  316.  
  317.     if (smspr_init_aliens(wave))
  318.         exit(100);  /** This should be impossible... **/
  319.     
  320.     for (i = 0; i < how_many; i++) {
  321.         if ((alien=smspr_create_alien())==NULL)
  322.             break;
  323.         spr_anim_set_time(alien, 0, 5, 
  324.                           alien_timeout + 20*i + (wave/HIT_DIVIDES_LIM)*50);
  325.             x = random(gr_max_x-40);
  326.             y = random(BOX_TOP-40);
  327.             if (random(2))
  328.                 y += BOX_BOT;
  329.  
  330.         spr_anim_set_location(alien, x,y);
  331.         set_limits(alien, x, y);
  332.         spr_anim_set_vector(alien,
  333.                             alien_speed,
  334.                             alien_speed-(random(2*alien_speed-1)));
  335.         alien_count++;
  336.     }
  337. }
  338.  
  339. /**********************************************************************
  340. * Initialize the wave'th alien attack.
  341. **********************************************************************/
  342. void wave_init(int wave)
  343. {
  344.     alien_count = 0;
  345.  
  346.     /* 6,8,10,12, 4,5,6,7, 5,6,7,8, 9,10,..., 60 */
  347.     if (wave/HIT_DIVIDES_LIM == 0)
  348.         max_aliens = 6 + wave*2;
  349.     else
  350.         max_aliens = wave - (wave/HIT_DIVIDES_LIM > 1)*2;
  351.     if (max_aliens>MAX_ALIEN)
  352.         max_aliens = MAX_ALIEN;
  353.     
  354.     /* 300,310,320,330, 340,360,380,400, 300,320,340,360,..., 100 */
  355.     if (wave/HIT_DIVIDES_LIM == 0)
  356.         alien_timeout = 300 + 10*wave;
  357.     else
  358.         alien_timeout = 400 + 20*(wave%HIT_DIVIDES_LIM) 
  359.                             - (wave/HIT_DIVIDES_LIM)*40;
  360.     if (alien_timeout<100)
  361.         alien_timeout = 100;
  362.     
  363.     /* 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,...*/
  364.     alien_speed = 2 + (wave/HIT_DIVIDES_LIM); 
  365.     
  366.     create_aliens(max_aliens);
  367.  
  368.     bonus = 2000 + wave*500;
  369.     if (wave==0)
  370.         score_inc = 5;
  371.     else
  372.         score_inc = 5*(wave*wave);
  373. }
  374.  
  375. /**********************************************************************
  376. * Initialize player into the left side of the screen
  377. **********************************************************************/
  378. void player_init(void)
  379. {
  380.     int x,y;
  381.  
  382.     x = BOX_LEF/2;
  383.     y = (BOX_TOP+BOX_BOT)/2;
  384.     spr_anim_start(player);
  385.     spr_anim_set_location(player, x, y);
  386.     set_limits(player, x, y);
  387.     spr_anim_set_time(player, 0, 0, 0);
  388.     spr_anim_set_vector(player, 0,0);
  389.     player_dying = player_dead = 0;
  390. }
  391.  
  392. /**********************************************************************
  393. * Initializes the game. (Variables, background, aliens, player)
  394. * Return: Player sprite if all OK, NULL otherwise.
  395. **********************************************************************/
  396. ANIM_SPRITE game_init(void)
  397. {
  398.     /** I want always the same alien sequence **/
  399.     srand(42);
  400.     
  401.     /** global variables **/
  402.     player_dead = player_immune = player_dying = 0;
  403.     lives = 5;
  404.     score = 0;
  405.     score_inc = 5;
  406.     bullets_flying = 0;
  407.     wave = cheat_wave;
  408.  
  409.     /*** middle box ***/
  410.     show_normal_box();
  411.     gr_setactivepage(0);
  412.     rectangle(BOX_LEF, BOX_TOP,  BOX_RIG, BOX_BOT);
  413.     gr_setactivepage(1);
  414.     rectangle(BOX_LEF, BOX_TOP,  BOX_RIG, BOX_BOT);
  415.  
  416.     /*** standard aliens ***/
  417.     wave_init(wave);
  418.     
  419.     /*** the player if necessary ***/
  420.     if (player==NULL)
  421.         player = smspr_create_player();
  422.     if (player==NULL)
  423.         return NULL;
  424.  
  425.     player_init();
  426.  
  427.     return player;
  428. }
  429.  
  430.  
  431. /**********************************************************************
  432. * Player collided with an alien.
  433. **********************************************************************/
  434. void kill_player(void)
  435. {
  436.     ANIM_SPRITE as;
  437.     ANIM_SPR_INFO *asi;
  438.  
  439.     asi = spr_anim_get_info(player);
  440.     spr_anim_stop(player);
  441.     player_dying = 1;
  442.     player_explosion = as = smspr_create_explosion();
  443.     if (as!=NULL) {
  444.         spr_anim_set_vector(as, asi->dx, asi->dy);
  445.         spr_anim_set_location(as, asi->x, asi->y);
  446.         set_limits(as, asi->x, asi->y);
  447.         spr_anim_set_time(as, 0,4,32);
  448.     }
  449.     else    /** weird, could not start an explosion. Kill player anyway **/
  450.         player_dead = 1;
  451. }
  452.  
  453. /**********************************************************************
  454. * Kill the given alien.
  455. **********************************************************************/
  456. void kill_alien(WORD w)
  457. {
  458.     ANIM_SPRITE as;
  459.     ANIM_SPR_INFO *asi;
  460.  
  461.     asi = spr_anim_get_info(aliens[w].as);
  462.  
  463.     spr_anim_stop(aliens[w].as);
  464.     aliens[w].active = 0;
  465.     alien_count--;
  466.  
  467.     as = smspr_create_explosion();
  468.     if (as!=NULL) {
  469.         spr_anim_set_vector(as, asi->dx, asi->dy);
  470.         spr_anim_set_location(as, asi->x, asi->y);
  471.         set_limits(as, asi->x, asi->y);
  472.         spr_anim_set_time(as, 0,4,16);
  473.     }
  474. }
  475.  
  476. /**********************************************************************
  477. * The alien fx handler.
  478. **********************************************************************/
  479. WORD alien_fx_handler(ANIM_SPRITE aspr, WORD fx, SPRITE spr)
  480. {
  481.     ANIM_SPR_INFO *asi;
  482.     WORD ret_code = SPR_ANIM_FX_RET_NOTHING;
  483.  
  484.     asi = spr_anim_get_info(aspr);
  485.  
  486.     switch (fx) {
  487.         case SPR_ANIM_FX_TIMEOUT:  /* mark that the alien should be divided */
  488.             aliens[asi->id].divide = 1;
  489.             ret_code = SPR_ANIM_FX_RET_RE_PUT;  /** show it last time **/
  490.             break;
  491.  
  492.         case SPR_ANIM_FX_HIT_X_LIMIT:
  493.             ret_code = handle_x_limit(aspr, asi);
  494.             break;
  495.  
  496.         case SPR_ANIM_FX_HIT_Y_LIMIT:
  497.             ret_code = handle_y_limit(aspr, asi);
  498.             break;
  499.  
  500.         default:
  501.             sound(2000);delay(1000);nosound();    /** this is a bug **/
  502.             break;
  503.     }
  504.     return ret_code;
  505. }
  506.  
  507. /**********************************************************************
  508. * The player fx handler.
  509. **********************************************************************/
  510. WORD player_fx_handler(ANIM_SPRITE aspr, WORD fx, SPRITE spr)
  511. {
  512.     ANIM_SPR_INFO *asi;
  513.     WORD ret_code = SPR_ANIM_FX_RET_NOTHING;
  514.     WORD w;
  515.  
  516.     asi = spr_anim_get_info(aspr);
  517.  
  518.     switch (fx) {
  519.         case SPR_ANIM_FX_TIMEOUT:
  520.             ret_code = SPR_ANIM_FX_RET_DELETE;
  521.             break;
  522.  
  523.         case SPR_ANIM_FX_HIT_X_LIMIT:
  524.             ret_code = handle_x_limit(aspr, asi);
  525.             break;
  526.  
  527.         case SPR_ANIM_FX_HIT_Y_LIMIT:
  528.             ret_code = handle_y_limit(aspr, asi);
  529.             break;
  530.  
  531.         case SPR_ANIM_FX_HIT_SPRITE:
  532.             w = spr_get_id(spr);
  533.             if (w<MAX_ALIEN) {
  534.                 kill_alien(w);
  535.                 if (!player_immune)
  536.                     kill_player();
  537.             }
  538.             break;
  539.  
  540.         default:
  541.             sound(2000);delay(1000);nosound();    /** this is a bug **/
  542.     }
  543.     return ret_code;
  544. }
  545.  
  546. /**********************************************************************
  547. * The bullet fx handler.
  548. **********************************************************************/
  549. WORD bullet_fx_handler(ANIM_SPRITE aspr, WORD fx, SPRITE spr)
  550. {
  551.     ANIM_SPR_INFO *asi;
  552.     WORD ret_code = SPR_ANIM_FX_RET_NOTHING;
  553.     WORD w;
  554.  
  555.     asi = spr_anim_get_info(aspr);
  556.  
  557.     switch (fx) {
  558.         case SPR_ANIM_FX_TIMEOUT:
  559.             bullets_flying--;
  560.             ret_code = SPR_ANIM_FX_RET_DESTROY;
  561.             break;
  562.  
  563.         case SPR_ANIM_FX_HIT_X_LIMIT:
  564.             ret_code = handle_x_limit(aspr, asi);
  565.             break;
  566.  
  567.         case SPR_ANIM_FX_HIT_Y_LIMIT:
  568.             ret_code = handle_y_limit(aspr, asi);
  569.             break;
  570.  
  571.         case SPR_ANIM_FX_HIT_SPRITE:
  572.             w = spr_get_id(spr);
  573.             if (w<MAX_ALIEN) {      /** kill alien or mark divide **/
  574.                 inc_score(score_inc);
  575.                 if (wave/HIT_DIVIDES_LIM && !aliens[w].divided)
  576.                     aliens[w].divide = aliens[w].shot = 1;
  577.                 else
  578.                     kill_alien(w);
  579.                 ret_code = SPR_ANIM_FX_RET_DESTROY;
  580.             }
  581.             break;
  582.  
  583.         default:
  584.             sound(2000);delay(1000);nosound();    /** this is a bug **/
  585.     }
  586.     if (ret_code==SPR_ANIM_FX_RET_DESTROY)
  587.         bullets_flying--;
  588.     return ret_code;
  589. }
  590.  
  591. /**********************************************************************
  592. * The explosion fx handler.
  593. **********************************************************************/
  594. WORD explo_fx_handler(ANIM_SPRITE aspr, WORD fx, SPRITE spr)
  595. {
  596.     ANIM_SPR_INFO *asi;
  597.     WORD ret_code = SPR_ANIM_FX_RET_NOTHING;
  598.  
  599.     asi = spr_anim_get_info(aspr);
  600.  
  601.     switch (fx) {
  602.         case SPR_ANIM_FX_TIMEOUT:
  603.             ret_code = SPR_ANIM_FX_RET_STOP;
  604.             break;
  605.  
  606.         case SPR_ANIM_FX_HIT_X_LIMIT:
  607.             ret_code = handle_x_limit(aspr, asi);
  608.             break;
  609.  
  610.         case SPR_ANIM_FX_HIT_Y_LIMIT:
  611.             ret_code = handle_y_limit(aspr, asi);
  612.             break;
  613.  
  614.         default:
  615.             sound(2000);delay(1000);nosound();    /** this is a bug **/
  616.     }
  617.  
  618.     if (ret_code == SPR_ANIM_FX_RET_STOP && aspr==player_explosion) {
  619.         player_explosion = NULL;
  620.         player_dead = 1;
  621.     }
  622.     if (ret_code == SPR_ANIM_FX_RET_STOP)
  623.         expls[asi->id - EXPLO_ID].active = 0;
  624.     
  625.     return ret_code;
  626. }
  627.  
  628. /**********************************************************************
  629. * Handle player input.
  630. * Return: 0 if game over, 1 otherwise.
  631. **********************************************************************/
  632. int handle_player(void)
  633. {
  634.     static int shot;
  635.     static int bullet_count = 0;
  636.     int dir;
  637.     ANIM_SPR_INFO *asi;
  638.     ANIM_SPRITE as;
  639.  
  640.     if (gr_keys[GR_KEY_ESC])
  641.         return 0;
  642.  
  643.     if (!player_dying) {
  644.  
  645.         /***** Rotations *****/
  646.         dir = 0;
  647.         if (gr_keys[GR_KEY_Z] || gr_keys[GR_KEY_ARROW_LEFT]) 
  648.             dir += 1;
  649.         if (gr_keys[GR_KEY_X] || gr_keys[GR_KEY_ARROW_RIGHT])
  650.             dir += -1;
  651.             
  652.         if (dir) {
  653.             asi = spr_anim_get_info(player);
  654.             asi->frame = (asi->frame+dir)%16;
  655.             spr_anim_set_time(player, asi->frame, -1,-1);
  656.         }
  657.  
  658.         /***** Thrust *****/
  659.         if (gr_keys[GR_KEY_M] || gr_keys[GR_KEY_ARROW_UP]) {
  660.             asi = spr_anim_get_info(player);
  661.             asi->dx += player_dirs[asi->frame].dx;
  662.             if (asi->dx > MAX_DX)
  663.                 asi->dx = MAX_DX;
  664.             if (asi->dx < -MAX_DX)
  665.                 asi->dx = -MAX_DX;
  666.             asi->dy += player_dirs[asi->frame].dy;
  667.             if (asi->dy > MAX_DY)
  668.                 asi->dy = MAX_DY;
  669.             if (asi->dy < -MAX_DY)
  670.                 asi->dy = -MAX_DY;
  671.             spr_anim_set_vector(player, asi->dx, asi->dy);
  672.         }
  673.  
  674.         /***** Missiles *****/
  675.         shot = 0;
  676.         if (gr_keys[GR_KEY_SPACE]
  677.             || gr_keys[GR_KEY_COMMA]
  678.             || gr_keys[GR_KEY_DOT]) {
  679.             shot = 1;
  680.             if (bullet_count<6 && bullets_flying<MAX_BULLET) {
  681.                 bullet_count++;
  682.                 bullets_flying++;
  683.                 asi = spr_anim_get_info(player);
  684.                 as = smspr_create_bullet();
  685.                 if (as!=NULL) {
  686.                     asi->dx = player_dirs[asi->frame].dx*5;
  687.                     asi->dy = player_dirs[asi->frame].dy*5;
  688.                     asi->x = asi->x + missile_deltas[asi->frame].dx;
  689.                     asi->y = asi->y + missile_deltas[asi->frame].dy;
  690.                     spr_anim_set_vector(as, asi->dx, asi->dy);
  691.                     spr_anim_set_location(as, asi->x, asi->y);
  692.                     set_limits(as, asi->x, asi->y);
  693.                     spr_anim_set_time(as, 0,0,0);
  694.                 }
  695.             }
  696.         }
  697.  
  698.         if (!shot && bullet_count>0)
  699.             bullet_count--;
  700.     }
  701.  
  702.     if (player_immune>0)
  703.         player_immune--;
  704.     
  705.     if (player_dead) {
  706.         dec_lives();
  707.         if (lives>0) {
  708.             player_dead = player_dying = 0;
  709.             player_immune = 15; /** player is invincible for 15 frames **/
  710.             spr_anim_start(player);
  711.             spr_anim_set_vector(player,0,0);
  712.             delay(200);
  713.         }
  714.     }
  715.  
  716.     return (lives>0);
  717. }
  718.  
  719.  
  720. /**********************************************************************
  721. * Handle special events which have been initialized from fx routines:
  722. * - divide mines. timeout divides a mine always into four new mines. 
  723. *       - In waves 0..LIM, aliens divide only after timeout,
  724. *       - In waves LIM..LIM-1, first shot divides initial mines into 
  725. *       two parts,
  726. *       - In waves LIM*2..LIM*3-1 first shot divides initial mines into
  727. *       three parts. 
  728. *       - In waves LIM*3..LIM*4-1  first shot divides initial mines into
  729. *       four parts. 
  730. *       - In waves LIM*4... all mines are divided into 1-4 parts by
  731. *       the first shot (this is hard!).
  732. *       The initial properties of mines are changed by wave_init.
  733. * - start new wave after all mines killed.
  734. **********************************************************************/
  735. void handle_advancement(void)
  736. {
  737.     ANIM_SPRITE as[4];
  738.     ANIM_SPR_INFO *asi;
  739.     int i,j;
  740.  
  741.     /** Divide the marked aliens **/
  742.     for (i=0; i<MAX_ALIEN; i++) {
  743.         if (aliens[i].divide) {
  744.             aliens[i].divide = 0;
  745.             if (aliens[i].active) {
  746.                 int div_count;
  747.                 
  748.                 if (aliens[i].shot)
  749.                     div_count = (1 + (wave/HIT_DIVIDES_LIM)%4);
  750.                 else
  751.                     div_count = 5;
  752.                 asi = spr_anim_get_info(aliens[i].as);
  753.                 spr_anim_stop(aliens[i].as);
  754.                 aliens[i].active = 0;
  755.                 aliens[i].shot = 0;
  756.                 alien_count--;
  757.                 for (i=0; i<4; i++) {
  758.                     if (i<div_count)
  759.                         as[i] = smspr_create_alien();
  760.                     else
  761.                         as[i] = NULL;
  762.                     if (as[i]!=NULL) {
  763.                         spr_anim_set_location(as[i], asi->x,asi->y);
  764.                         spr_anim_set_time(as[i], 0, 3, alien_timeout+10*i);
  765.                         set_limits(as[i], asi->x, asi->y);
  766.                         asi = spr_anim_get_info(as[i]);
  767.                         if (wave/HIT_DIVIDES_LIM < 4 || div_count < 5)
  768.                             aliens[asi->id].divided = 1;
  769.                         alien_count++;
  770.                     }
  771.                 }
  772.                 j = random(4);
  773.                 if (as[0]!=NULL)
  774.                     spr_anim_set_vector(as[0],  
  775.                       mine_dir[j].dx*alien_speed, mine_dir[j].dy*alien_speed);
  776.                 j = (j+1)&3;
  777.                 if (as[1]!=NULL)
  778.                     spr_anim_set_vector(as[1],
  779.                       mine_dir[j].dx*alien_speed, mine_dir[j].dy*alien_speed);
  780.                 j = (j+1)&3;
  781.                 if (as[2]!=NULL)
  782.                     spr_anim_set_vector(as[2],
  783.                       mine_dir[j].dx*alien_speed, mine_dir[j].dy*alien_speed);
  784.                 j = (j+1)&3;
  785.                 if (as[3]!=NULL)
  786.                     spr_anim_set_vector(as[3],
  787.                       mine_dir[j].dx*alien_speed, mine_dir[j].dy*alien_speed);
  788.             }
  789.         }
  790.     }
  791.  
  792.     /** start the next alien wave if all aliens killed **/
  793.     if (alien_count<=0) {
  794.         if (player_dying)
  795.             dec_lives();
  796.         if (lives>0) {
  797.             for (i=1; i<30; i++) {
  798.                 gr_setactivepage(spr_anim_next_pass()^1);
  799.                 spr_regulate_speed();
  800.                 gr_center_printf(NEXT_WAVE_Y, NEXT_WAVE1, bonus);
  801.                 gr_setactivepage(spr_anim_next_pass()^1);
  802.                 spr_regulate_speed();
  803.                 gr_center_printf(NEXT_WAVE_Y, NEXT_WAVE2, bonus);
  804.             }
  805.             inc_score(bonus);
  806.             wave++;
  807.             gr_dual_center_printf(NEXT_WAVE_Y, CURRENT_WAVE, wave+1);
  808.             wave_init(wave);
  809.             player_init();
  810.         }
  811.     }
  812.     if (bonus>0)
  813.         bonus--;
  814. }
  815.  
  816. /**********************************************************************
  817. * Animate f frames. Return also if Esc or space pressed.
  818. **********************************************************************/
  819. void animated_wait(int f)
  820. {
  821.     int i;
  822.     
  823.     for (i=1; i<f && !gr_keys[GR_KEY_ESC] && !gr_keys[GR_KEY_SPACE]; i++) {
  824.         gr_setactivepage(spr_anim_next_pass());
  825.         spr_regulate_speed();
  826.     }    
  827. }
  828.  
  829. /**********************************************************************
  830. * Show high scores and wait for a space or Esc.
  831. **********************************************************************/
  832. int idle(void)
  833. {
  834.     int i;
  835.     
  836.     show_hiscores();
  837.     gr_dual_center_printf(GAME_OVER2_Y, GAME_OVER2);
  838.     gr_dual_center_printf(GAME_OVER3_Y, GAME_OVER3);
  839.     do {
  840.         gr_setactivepage(spr_anim_next_pass());
  841.         spr_regulate_speed();
  842.         delay(10);
  843.         
  844.         if (gr_keys[GR_KEY_C] && gr_keys[GR_KEY_H] &&  gr_keys[GR_KEY_E] &&
  845.             gr_keys[GR_KEY_A] && gr_keys[GR_KEY_T] && !gr_keys[GR_KEY_R]) {
  846.             cheat_wave++;
  847.             sound(880); delay(250); nosound(); delay(250);
  848.         }
  849.             
  850.         i = gr_inkey();
  851.     } while (i!=27 && i!=' ');
  852.  
  853.     return i;
  854. }
  855.  
  856. /**********************************************************************
  857. * Game over text. Wait for a keypress. Init new game or exit.
  858. * Return: 0 if user quitted.
  859. **********************************************************************/
  860. int game_over(void)
  861. {
  862.     int i;
  863.     
  864.     gr_dual_center_printf(GAME_OVER_Y, GAME_OVER);
  865.     animated_wait(40);
  866.  
  867.     if (player_dead) {  /*** Only heroes make it to the Hall of Fame ***/
  868.         i = smscores_check(score);
  869.         if (i!=SCORE_COUNT) {
  870.             gr_setactivepage(0);
  871.             gr_setvisualpage(0);
  872.             se.score = score;
  873.             se.name[0] = '\0';
  874.             smscores_add(&se);
  875.             ask_name(&se, i);
  876.             smscores_add(&se);
  877.         }
  878.         else 
  879.             animated_wait(80);
  880.     }
  881.  
  882.     i = idle();
  883.     
  884.     if (i==27)
  885.         return 0;
  886.     else {  /** kill surviving aliens and start a new game **/
  887.         for (i=0; i<MAX_ALIEN; i++)
  888.             if (aliens[i].active) {
  889.                 spr_anim_stop(aliens[i].as);
  890.                 aliens[i].active = 0;
  891.             }
  892.         game_init();
  893.     }
  894.     return 1;
  895. }
  896.  
  897. /** farcore for saving background of MAX_ALIEN 2-frame aliens,  **/
  898. /** 16 player shapes, 20 bullets and 20 4-frame explosions.     **/
  899. #define MEM_NEEDED (80L*2*MAX_ALIEN*2 + \
  900.                     96L*(sprite_resolution*2+2)*16 + \
  901.                     8L*2*20 + \
  902.                     120L*2*MAX_EXPL*4)
  903.  
  904. /**********************************************************************
  905. * The main program
  906. **********************************************************************/
  907. void main(int argc, char **argv)
  908. {
  909.     int i,j,x,y;
  910.  
  911.     puts("StarMines - a sprite toolkit demonstration game");
  912.     puts("      Copyright (C) 1991 Jari Karjala\n");
  913.  
  914.     if (argc>1)
  915.         sprite_resolution = atoi(argv[1]);
  916.  
  917.     if (argc>2)  /** watch out for those leaks **/
  918.         printf("core %ld, farcore %ld", (long)coreleft(), farcoreleft());
  919.  
  920.     if (farcoreleft() < MEM_NEEDED) {
  921.         printf("Not enough memory (%d kB needed), sorry!",
  922.                140 + MEM_NEEDED/1024L);
  923.         exit(5);
  924.     }
  925.  
  926.     gr_detect(GR_TYPE_SPR, &i, &j);
  927.     if (i == -1) {
  928.         puts("Unsupported graphics mode, sorry!");
  929.         exit(10);
  930.     }
  931.     #ifdef BIND_DRIVERS
  932.     {
  933.         extern void HERCDRIVERPROC(void);
  934.         extern void EGAVGADRIVERPROC(void);
  935.  
  936.         if (registerbgidriver(HERCDRIVERPROC)<0)
  937.             puts(grapherrormsg(graphresult()));
  938.         if (registerbgidriver(EGAVGADRIVERPROC)<0)
  939.             puts(grapherrormsg(graphresult()));
  940.     }
  941.     #endif
  942.  
  943.     gr_text_mode = GR_MODE_CLEAR_FAST;
  944.     gr_start(&i, &j);
  945.     spr_initialize(i);
  946.     cleardevice();
  947.     smscores_init();
  948.  
  949.     srand(42);   /** I want always the same stars **/
  950.  
  951.     /** Oh my god, it is full of stars... **/
  952.     for (i=0; i<80; i++) {
  953.         x=random(gr_max_x);
  954.         y=random(gr_max_y);
  955.         gr_setactivepage(0);
  956.         putpixel(x, y, getmaxcolor());
  957.         gr_setactivepage(1);
  958.         putpixel(x, y, getmaxcolor());
  959.     }
  960.  
  961.     if (game_init()==NULL) {
  962.         gr_dual_xy_printf(0,0,"Initialization failed, press enter...");
  963.         while(gr_inkey()==0)
  964.             ;
  965.         exit(20);
  966.     }
  967.  
  968.     gr_start_kbd_grab();
  969.     while (i) {                 /** the game main loop **/
  970.         i = 1;
  971.         while (i) {             /** play until player dead or Esc pressed **/
  972.             i = handle_player();
  973.             gr_setactivepage(spr_anim_next_pass());
  974.             spr_regulate_speed();
  975.             handle_advancement();
  976.             if (argc>2)       /** watch out for those memory leaks **/
  977.                 gr_xy_printf(0,0,"%6lu %6lu",(long)coreleft(), farcoreleft());
  978.         }
  979.         i = game_over();
  980.     }
  981.     gr_end_kbd_grab();
  982.     gr_end();
  983.     exit(0);
  984. }
  985.