home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 002.lha / YaBoing.c < prev    next >
C/C++ Source or Header  |  1986-05-05  |  14KB  |  478 lines

  1. /* This is Yet Another Boing, "YaBoing." A (game) program for the Amiga.
  2.  
  3.    By Ali T. Ozer (Ali@score.stanford.edu), Sep 21, 1986 
  4.  
  5.    This game owes a lot to Leo L. Schwab's "Oing!," where little sprite
  6.    versions of the famous "Boing!" demo ball bounced around your screen...
  7.    This program demonstrates some hardware sprite stuff, collisions, colors,
  8.    etc...
  9.  
  10.    Try to get the red balls with your mouse (+1) while avoiding the
  11.    green ones (-1). Your score is displayed after 100 red balls have been
  12.    thrown to the screen and the game restarts.
  13.    
  14.    The game can be interrupted at any point by just deactivating the
  15.    window. The sprites disappear, and you can do whatever you want. To con-
  16.    tinue, simply click in the YaBoing window, and voila, you're back in your
  17.    game where you left it. YaBoing is a great game to have running in the
  18.    background as you compile or do other time consuming things. Note
  19.    that YaBoing does not start automatically --- You have to click
  20.    on the window to get it running... This allows you to start it up from
  21.    your startup sequence, for example! 
  22.  
  23.    The game sets its priority to -1, so it should not steal many CPU cycles
  24.    from other processes. It also uses only 10K memory + whatever your
  25.    stack size is. Thus you can afford to have it in the background and
  26.    come back to it from time to time. Of course, if your other processes
  27.    grap the cpu, then YaBoing will suffer. But, hey, it just makes the game
  28.    more random! You can set the priority yourself by specifying the 
  29.    priority (-128..+127) as the argument to the "yaboing" command.
  30.  
  31.    I compiled YaBoing! with Manx 3.20a, without the +l option. Haven't
  32.    tried it with Lattice.
  33.  
  34.    -Ali Ozer
  35. */
  36.    
  37. #include <exec/types.h>
  38. #include <exec/memory.h>
  39. #include <intuition/intuition.h>
  40. #include <graphics/sprite.h>
  41. #include <hardware/custom.h>
  42.  
  43. /* The ball image... Note that in YaBoing the balls do not rotate as */
  44. /* nicely as in Boing! or Oing!. The rotation is just obtained by rotating */
  45. /* the 3 colors available for sprites, and that doesn't provide for very */
  46. /* smooth movement... */
  47.  
  48. UWORD ballimage[] = {
  49. 0x0660, 0x0780,
  50. 0x1FF8, 0x1960,
  51. 0x399C, 0x2778,
  52. 0x6666, 0x1DDE,
  53. 0x4E72, 0x3DCE,
  54. 0xDE7B, 0x39E7,
  55. 0x39E7, 0xE79C,
  56. 0x79E7, 0xE79E,
  57. 0x79E7, 0xE79E,
  58. 0x39E7, 0xE79C,
  59. 0xDE7B, 0x39E7,
  60. 0x4E72, 0x3DCE,
  61. 0x6666, 0x1DDE,
  62. 0x399C, 0x2778,
  63. 0x1FF8, 0x1960,
  64. 0x0660, 0x0780
  65. };
  66.  
  67. #define DEFPRIORITY -1
  68.  
  69. /* Pointer to base of custom registers. Note that you're supposed to */
  70. /* be able to simply do "extern struct Custom custom" too... */
  71.  
  72. struct Custom *cstm = 0xdff000;
  73.  
  74. /* Collision bits in register CLXDAT. The three bits below correspond */
  75. /* to collisions between sprite 0 (the mouse) and sprites 2, 4, and 6, */
  76. /* respectively. */
  77.  
  78. UWORD colmask[3] = {0x0200, 0x0400, 0x0800};
  79.  
  80. /* The number of "red/white" sprites that get thrown to the screen */
  81. /* during the game. This is effectively the max score you can get. */
  82.  
  83. #define MAXSPRITES 100
  84.  
  85. /* Various sprite info. Note that in the sides we let the sprite */
  86. /* disappear completely before it's considered out of bounds while */
  87. /* at top of the screen we detect as soon as we reach the top line of the */
  88. /* screen (so we can "bounce" the ball back). */
  89.  
  90. #define SPRHEIGHT 16
  91. #define XMAX 640
  92. #define YMAX 200
  93. #define XMIN -16
  94. #define YMIN 0
  95.  
  96. /* Sprite modes. */
  97.  
  98. #define SPRITEINACTIVE 0
  99. #define SPRITEACTIVE   1
  100. #define SPRITEHIT      2
  101.  
  102. void *OpenLibrary(), *OpenWindow(), *AllocMem(), *ViewPortAddress();
  103. long GetSprite(), VBeamPos();
  104.  
  105. /* The window. Note that the window is really not good for much, except */
  106. /* for showing the score on its title bar and letting the user activate/ */
  107. /* deactivate the window (to stop/continue the game). */
  108.  
  109. struct NewWindow newwindow = {
  110.   400, 15, 200, 10, -1, -1,
  111.   CLOSEWINDOW | ACTIVEWINDOW | INACTIVEWINDOW,
  112.   WINDOWCLOSE | WINDOWDEPTH | WINDOWDRAG | ACTIVEWINDOW | INACTIVEWINDOW,
  113.   NULL, NULL,
  114.   (UBYTE *) "YaBoing!",
  115.   NULL, NULL, 0, 0, 0, 0,
  116.   WBENCHSCREEN
  117. };
  118.  
  119. /* The score, sent to the title bar of the window... */
  120.  
  121. UBYTE scorestr[11];
  122.  
  123. /* Number of sprites. Of course it's rather silly having a constant for */
  124. /* this, as parts of the program depend on the fact that there are */
  125. /* 3 sprites, corresponding to hardware sprites 2, 4, and 6... Oh well! */
  126.  
  127. #define NUMSPR 3
  128.  
  129. struct Window    *win;
  130. struct ViewPort    *vp;
  131.  
  132. /* The info we have about a sprite. */
  133. /*    vx, vy are velocity, ax, ay are acceleration, px, py are */
  134. /*    position, mode is current status (sprite is moving, dead, etc), */
  135. /*    and code is a value dependant on the value of mode (how long */
  136. /*    it's been dead, moving, etc...). */
  137.  
  138. struct sprrec {
  139.   struct SimpleSprite actspr;
  140.   int vx, vy, ax, ay, px, py, mode, code;
  141. } spr[NUMSPR];
  142.  
  143. /* For modding by 3, we do a table lookup. (Idea taken from Leo */
  144. /* Schwab's idx[] in "Oing!"). */
  145.  
  146. int mod3[] = {0, 1, 2, 0, 1, 2};
  147.  
  148. /* Coloroffset and rotatecount determine where in the rotation we are */
  149. /* and how many cycles are left before we rotate. Score is pretty */
  150. /* obvious, and sprcnt is the number of red/white sprites sent to the */
  151. /* screen... */
  152.  
  153. int coloroffset, rotatecount, score, sprcnt;
  154.  
  155. /* ROTATECOUNT determines the number of cycles between a single */
  156. /* rotation of the ball (not a full rotation!) */
  157.  
  158. #define ROTATECOUNT 10
  159.  
  160. /* 3 second (150 tick) delay between games */
  161.  
  162. #define NEWGAMEDELAY 150L
  163.  
  164. /* When the user deactivates the window, the sprites are deactivated */
  165. /* and the games stops until user activates the window again. */
  166.  
  167. int spritesactive;
  168.  
  169. void *GfxBase, *IntuitionBase;
  170.  
  171. /* Pointer to the base of the CHIP memory obtained for the sprites... */
  172.  
  173. UWORD *sprbuf;
  174.  
  175. main (argc, argv)
  176. int argc;
  177. char **argv;
  178. {
  179.     register int cnt;
  180.     register struct sprrec *tmpspr;
  181.     int i, dir;      /* During sprite creation, direction it enters from */
  182.     UWORD clxdat;    /* Value of collision register */
  183.     ULONG msgclass;  /* Message class from Intuition message */
  184.     struct IntuiMessage *msg, *GetMsg();
  185.     struct Task *thistask, *FindTask ();
  186.  
  187.     /* First set the priority of this task. */
  188.  
  189.     if (thistask = FindTask (NULL)) {
  190.       if (argc < 2 || (cnt = atoi (argv[1])) < -128 || cnt > 127)
  191.         cnt = DEFPRIORITY;
  192.       SetTaskPri (thistask, (long)cnt);
  193.     }
  194.  
  195.     OpenStuff ();
  196.  
  197.     InitSprites ();
  198.  
  199.     /* Detect collisions between sprite 0 and sprites 2, 4, 6 */
  200.  
  201.     cstm->clxcon = 0xF000;
  202.  
  203.     sprcnt = 0; score = 0;
  204.  
  205.     while (1) {
  206.  
  207.     /* If the game has ended, the simply display the score and wait */
  208.         /* for a message... */
  209.  
  210.     if (sprcnt >= MAXSPRITES) {
  211.       ChgSpriteHeight (0);
  212.       for (cnt = 0; cnt < NUMSPR; cnt++) spr[cnt].mode = SPRITEINACTIVE;
  213.       ShowScore ();
  214.       Delay (NEWGAMEDELAY);
  215.       sprcnt = 0; score = 0;
  216.         }
  217.  
  218.     while (msg = GetMsg (win->UserPort)) {
  219.       msgclass = msg->Class;
  220.        ReplyMsg (msg);
  221.       switch (msgclass) {
  222.             case CLOSEWINDOW: CloseStuff (0); 
  223.         case ACTIVEWINDOW: 
  224.               if (!spritesactive) ChgSpriteHeight (SPRHEIGHT);
  225.               spritesactive = TRUE;
  226.               break;
  227.             case INACTIVEWINDOW:
  228.           if (spritesactive) ChgSpriteHeight (0);
  229.           spritesactive = FALSE;
  230.           break;
  231.             default: ;
  232.           }
  233.         }
  234.  
  235.     if (!spritesactive) Wait (1L << win->UserPort->mp_SigBit);
  236.  
  237.         else {
  238.      
  239.          for (cnt = 0; cnt < NUMSPR; cnt++)  {
  240.  
  241.           tmpspr = &spr[cnt];
  242.  
  243.           switch (tmpspr->mode) {
  244.  
  245.            case SPRITEINACTIVE:
  246.             if (tmpspr->code++ > 0 && sprcnt < MAXSPRITES) {
  247.           if (cnt < 2) sprcnt++;    /* One more sprite gone... */
  248.           dir = (RndNum(2) ? 1 : -1);
  249.               tmpspr->mode = SPRITEACTIVE;
  250.               tmpspr->code = 0;
  251.               tmpspr->vx   = dir * (10 + RndNum (12) + 6 * (cnt != 0));
  252.               tmpspr->vy   = (3 - RndNum(7));
  253.               tmpspr->ax   = dir * (RndNum(6) - 2) * RndNum (2);
  254.               tmpspr->ay   = (-1 + RndNum(3)) * cnt;
  255.               tmpspr->px   = ((dir == 1) ? (XMIN + 1) : (XMAX - 1));
  256.               tmpspr->py   = ((tmpspr->vy > 0) ? 10 : 100) + RndNum(91);
  257.           tmpspr->actspr.height = SPRHEIGHT;
  258.             };
  259.             break;
  260.  
  261.        case SPRITEHIT:
  262.             if (--(tmpspr->code) == 0) {      /* Sprite now inactive */
  263.               tmpspr->actspr.height = 0;
  264.           tmpspr->mode = SPRITEINACTIVE;
  265.               tmpspr->code = - RndNum(20);
  266.           DoColors ();
  267.             } else 
  268.               SetRGB4 (vp, (long)(21 + (cnt << 2) + (tmpspr->code >> 3)),
  269.                        0L, 0L, 0L);  /* Black! (as the sprite dies...) */
  270.             break;
  271.               
  272.            case SPRITEACTIVE:
  273.         /* The following two lines depend on the fact that ">>" works */
  274.             /* correctly on signed quantities here. */
  275.             tmpspr->px += (tmpspr->vx >> 2);
  276.             tmpspr->py += (tmpspr->vy >> 3);  
  277.         tmpspr->vx += tmpspr->ax;
  278.         tmpspr->vy += tmpspr->ay;
  279.             if (tmpspr->px < XMIN || tmpspr->px > XMAX ||
  280.                 tmpspr->py > YMAX) {
  281.               tmpspr->actspr.height = 0;
  282.           tmpspr->mode = SPRITEINACTIVE;
  283.               tmpspr->code = - RndNum(20);
  284.           DoColors ();
  285.             } else if (tmpspr->py < YMIN) {
  286.               tmpspr->py = 0;
  287.               tmpspr->vy = (-tmpspr->vy) * (1 + RndNum(2));
  288.             };
  289.         switch (RndNum(128)) {
  290.              case 0: tmpspr->ax = -(tmpspr->ax * (RndNum(2) + 1));
  291.          case 1: tmpspr->ay = -tmpspr->ay; break;
  292.          case 2: tmpspr->ay++;
  293.              case 3: tmpspr->ax++; break;
  294.              default: ;
  295.         }
  296.             break;
  297.  
  298.           default: ;
  299.     
  300.          }
  301.  
  302.         }
  303.  
  304.     for (cnt = 0; cnt < NUMSPR; cnt++) 
  305.           MoveSprite (vp, &(spr[cnt].actspr), 
  306.                       (long)spr[cnt].px, (long)spr[cnt].py);
  307.  
  308.         clxdat = cstm->clxdat;
  309.  
  310.     for (cnt = 0; cnt < NUMSPR; cnt++) 
  311.       if ((clxdat & colmask[cnt]) && (spr[cnt].mode == SPRITEACTIVE)) {
  312.             spr[cnt].mode = SPRITEHIT;
  313.             spr[cnt].code = 24;
  314.         if (cnt == 2) {   /* Hit a negative one, set color to gray */
  315.            score -= 1;
  316.           for (i = 0; i < 3; i++)
  317.                 SetRGB4(vp, (long)(21 + (cnt << 2) + i), 10L, 10L, 10L);
  318.             } else {
  319.            score += 1;
  320.           for (i = 0; i < 3; i++)
  321.                 SetRGB4(vp, (long)(21 + (cnt << 2) + i), 15L, 14L, 0L);
  322.         }
  323.           }
  324.  
  325.         WaitTOF ();
  326.  
  327.         if (--rotatecount < 0) {
  328.           coloroffset = mod3[coloroffset + 1];
  329.           DoColors ();
  330.           rotatecount = ROTATECOUNT;
  331.         }
  332.  
  333.        }      
  334.     }
  335. }        
  336.  
  337.  
  338. ChgSpriteHeight (newheight)
  339. int newheight;
  340. {
  341.   register int cnt;
  342.  
  343.   for (cnt = 0; cnt < NUMSPR; cnt++) {
  344.     spr[cnt].actspr.height = newheight;
  345.     ChangeSprite (vp, &(spr[cnt].actspr), spr[cnt].actspr.posctldata);
  346.   }
  347. }  
  348.  
  349.  
  350. OpenStuff ()
  351. {
  352.     register int i;
  353.  
  354.     if (!(IntuitionBase = OpenLibrary ("intuition.library", 0))) 
  355.           CloseStuff (1);
  356.     if (!(GfxBase = OpenLibrary ("graphics.library", 0))) 
  357.           CloseStuff (1);
  358.     if (!(win = OpenWindow (&newwindow))) 
  359.           CloseStuff (1);
  360.  
  361.     vp = ViewPortAddress (win);
  362.  
  363.         spritesactive = FALSE;
  364.     coloroffset = 0;
  365.         rotatecount = ROTATECOUNT;
  366. }
  367.  
  368. CloseStuff (exitcode)
  369. int exitcode;
  370. {
  371.     register int cnt;
  372.  
  373.     for (cnt = 0; cnt < NUMSPR; cnt++) 
  374.       if (spr[cnt].actspr.num) FreeSprite ((long) spr[cnt].actspr.num);
  375.     if (sprbuf)
  376.         FreeMem (sprbuf, (long)((SPRHEIGHT * 2 + 2) * NUMSPR));
  377.     if (win) CloseWindow (win);
  378.     if (GfxBase) CloseLibrary (GfxBase);
  379.     if (IntuitionBase) CloseLibrary (IntuitionBase);
  380.     exit (exitcode);
  381. }
  382.  
  383.  
  384. /* This routine mostly from Leo Schwab. It obtains the actual hardware */
  385. /* sprites, and the chip memory to put the sprite images in, and the */
  386. /* copies the "ballimage" into the sprite area... */
  387.  
  388. InitSprites ()
  389. {
  390.     UWORD *cw, *curspr, *ball;
  391.     int cnt, line;
  392.  
  393.         /* We use 3 sprites, 0..2, corresponding to system sprites 2,4,6 */
  394.  
  395.     if (!(sprbuf = 
  396.             AllocMem ((long)((SPRHEIGHT * 2 + 4) * NUMSPR * 2), MEMF_CHIP)))
  397.           CloseStuff (1);
  398.  
  399.     cw = sprbuf;
  400.  
  401.     for (cnt = 0; cnt < NUMSPR; cnt++) {
  402.           if (GetSprite (&(spr[cnt].actspr), (long) ((cnt << 1) + 2)) == -1)
  403.             CloseStuff (1);
  404.           spr[cnt].mode = SPRITEINACTIVE;
  405.       spr[cnt].code = -ROTATECOUNT;
  406.           spr[cnt].actspr.height = 0;
  407.       ball = ballimage;
  408.        curspr = cw;
  409.         *cw++ = 0; *cw++ = 0;
  410.       for (line = 0; line < SPRHEIGHT * 2; line++) *cw++ = *ball++;
  411.       *cw++ = 0; *cw++ = 0;
  412.           ChangeSprite (vp, &(spr[cnt].actspr), curspr);
  413.     }
  414. }
  415.  
  416. /* DoColors will rotate the colors for the 3 sprites. Rotation consists */
  417. /* of putting color 2 into 1, 3 into 2, and 1 into 3. The values are */
  418. /* hardwired below, so we don't actually go in and obtain the values */
  419. /* from the window... */
  420.  
  421. DoColors ()
  422. {
  423.   register int cnt, creg;
  424.   
  425.   for (cnt = 0; cnt < NUMSPR-1; cnt++) 
  426.     if (spr[cnt].mode != SPRITEHIT) {
  427.       creg = 21 + (cnt << 2);
  428.       SetRGB4 (vp, (long)(creg + coloroffset), 15L, 0L, 0L);
  429.       SetRGB4 (vp, (long)(creg + mod3[coloroffset+1]), 15L, 1L, 1L);
  430.       SetRGB4 (vp, (long)(creg + mod3[coloroffset+2]), 15L, 14L, 13L);
  431.     }      
  432.   if (spr[2].mode != SPRITEHIT) {
  433.     SetRGB4 (vp, (long)(29 + coloroffset), 2L, 12L, 0L);
  434.     SetRGB4 (vp, (long)(29 + mod3[coloroffset+1]), 0L, 11L, 1L);
  435.     SetRGB4 (vp, (long)(29 + mod3[coloroffset+2]), 15L, 14L, 13L);
  436.   }      
  437. }
  438.  
  439. ShowScore ()
  440. {
  441.   register UBYTE *loc = &scorestr[7];
  442.  
  443.   strcpy (scorestr, "SCORE:     ");
  444.   if (score < 0) {*loc++ = '-'; score = -score;};
  445.   if (score > 99) *loc++ = (score / 100) + '0';
  446.   if (score > 9)  *loc++ = (score / 10) % 10 + '0';
  447.   *loc++ = (score % 10) + '0';
  448.   *loc = 0;
  449.   SetWindowTitles (win, scorestr, -1L);
  450.   DisplayBeep (NULL);   /* You might want to get rid of this!!! */
  451.   WindowToFront (win);
  452. }  
  453.   
  454. ULONG seed;
  455.  
  456. /* Random number generator, translated into C from rnd.s */
  457. /* provided with Oing! Returns an integer 0..max-1. Don't know how */
  458. /* well it works, but the game seems random enough. */
  459.  
  460. RndNum (max)
  461. UWORD max;
  462. {
  463.   int result = (int)(seed % max);
  464.   long datevec[3], *DateStamp();
  465.   
  466.   if (seed == 0L) {
  467.     DateStamp (datevec);
  468.     seed = (UWORD) (datevec[1] * datevec[2]);
  469.   } else {
  470.     if (seed > 0x80000000L) seed = ((seed << 1) ^ 0x1D872B41L);
  471.     else seed <<= 1;
  472.   }
  473.  
  474.   return (result);
  475. }
  476.  
  477. /* YaBoing, by Ali T. Ozer */
  478.