[net.micro.amiga] A crazy

ali@navajo.STANFORD.EDU (Ali Ozer) (09/22/86)

/* This is the only file, remove the mail header & compile! */  

/* This is Yet Another Boing, "YaBoing." A (game) program for the Amiga.

   By Ali T. Ozer (Ali@score.stanford.edu), Sep 21, 1986 

   This game owes a lot to Leo L. Schwab's "Oing!," where little sprite
   versions of the famous "Boing!" demo ball bounced around your screen...
   This program demonstrates some hardware sprite stuff, collisions, colors,
   etc...

   Try to get the red balls with your mouse (+1) while avoiding the
   green ones (-1). Your score is displayed after 100 red balls have been
   thrown to the screen and the game restarts.
   
   The game can be interrupted at any point by just deactivating the
   window. The sprites disappear, and you can do whatever you want. To con-
   tinue, simply click in the YaBoing window, and voila, you're back in your
   game where you left it. YaBoing is a great game to have running in the
   background as you compile or do other time consuming things. Note
   that YaBoing does not start automatically --- You have to click
   on the window to get it running... This allows you to start it up from
   your startup sequence, for example! 

   The game sets its priority to -1, so it should not steal many CPU cycles
   from other processes. It also uses only 10K memory + whatever your
   stack size is. Thus you can afford to have it in the background and
   come back to it from time to time. Of course, if your other processes
   grap the cpu, then YaBoing will suffer. But, hey, it just makes the game
   more random! You can set the priority yourself by specifying the 
   priority (-128..+127) as the argument to the "yaboing" command.

   I compiled YaBoing! with Manx 3.20a, without the +l option. Haven't
   tried it with Lattice.

   -Ali Ozer
*/
   
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/sprite.h>
#include <hardware/custom.h>

/* The ball image... Note that in YaBoing the balls do not rotate as */
/* nicely as in Boing! or Oing!. The rotation is just obtained by rotating */
/* the 3 colors available for sprites, and that doesn't provide for very */
/* smooth movement... */

UWORD ballimage[] = {
0x0660, 0x0780,
0x1FF8, 0x1960,
0x399C, 0x2778,
0x6666, 0x1DDE,
0x4E72, 0x3DCE,
0xDE7B, 0x39E7,
0x39E7, 0xE79C,
0x79E7, 0xE79E,
0x79E7, 0xE79E,
0x39E7, 0xE79C,
0xDE7B, 0x39E7,
0x4E72, 0x3DCE,
0x6666, 0x1DDE,
0x399C, 0x2778,
0x1FF8, 0x1960,
0x0660, 0x0780
};

#define DEFPRIORITY -1

/* Pointer to base of custom registers. Note that you're supposed to */
/* be able to simply do "extern struct Custom custom" too... */

struct Custom *cstm = 0xdff000;

/* Collision bits in register CLXDAT. The three bits below correspond */
/* to collisions between sprite 0 (the mouse) and sprites 2, 4, and 6, */
/* respectively. */

UWORD colmask[3] = {0x0200, 0x0400, 0x0800};

/* The number of "red/white" sprites that get thrown to the screen */
/* during the game. This is effectively the max score you can get. */

#define MAXSPRITES 100

/* Various sprite info. Note that in the sides we let the sprite */
/* disappear completely before it's considered out of bounds while */
/* at top of the screen we detect as soon as we reach the top line of the */
/* screen (so we can "bounce" the ball back). */

#define SPRHEIGHT 16
#define XMAX 640
#define YMAX 200
#define XMIN -16
#define YMIN 0

/* Sprite modes. */

#define SPRITEINACTIVE 0
#define SPRITEACTIVE   1
#define SPRITEHIT      2

void *OpenLibrary(), *OpenWindow(), *AllocMem(), *ViewPortAddress();
long GetSprite(), VBeamPos();

/* The window. Note that the window is really not good for much, except */
/* for showing the score on its title bar and letting the user activate/ */
/* deactivate the window (to stop/continue the game). */

struct NewWindow newwindow = {
  400, 15, 200, 10, -1, -1,
  CLOSEWINDOW | ACTIVEWINDOW | INACTIVEWINDOW,
  WINDOWCLOSE | WINDOWDEPTH | WINDOWDRAG | ACTIVEWINDOW | INACTIVEWINDOW,
  NULL, NULL,
  (UBYTE *) "YaBoing!",
  NULL, NULL, 0, 0, 0, 0,
  WBENCHSCREEN
};

/* The score, sent to the title bar of the window... */

UBYTE scorestr[11];

/* Number of sprites. Of course it's rather silly having a constant for */
/* this, as parts of the program depend on the fact that there are */
/* 3 sprites, corresponding to hardware sprites 2, 4, and 6... Oh well! */

#define NUMSPR 3

struct Window	*win;
struct ViewPort	*vp;

/* The info we have about a sprite. */
/*    vx, vy are velocity, ax, ay are acceleration, px, py are */
/*    position, mode is current status (sprite is moving, dead, etc), */
/*    and code is a value dependant on the value of mode (how long */
/*    it's been dead, moving, etc...). */

struct sprrec {
  struct SimpleSprite actspr;
  int vx, vy, ax, ay, px, py, mode, code;
} spr[NUMSPR];

/* For modding by 3, we do a table lookup. (Idea taken from Leo */
/* Schwab's idx[] in "Oing!"). */

int mod3[] = {0, 1, 2, 0, 1, 2};

/* Coloroffset and rotatecount determine where in the rotation we are */
/* and how many cycles are left before we rotate. Score is pretty */
/* obvious, and sprcnt is the number of red/white sprites sent to the */
/* screen... */

int coloroffset, rotatecount, score, sprcnt;

/* ROTATECOUNT determines the number of cycles between a single */
/* rotation of the ball (not a full rotation!) */

#define ROTATECOUNT 10

/* 3 second (150 tick) delay between games */

#define NEWGAMEDELAY 150L

/* When the user deactivates the window, the sprites are deactivated */
/* and the games stops until user activates the window again. */

int spritesactive;

void *GfxBase, *IntuitionBase;

/* Pointer to the base of the CHIP memory obtained for the sprites... */

UWORD *sprbuf;

main (argc, argv)
int argc;
char **argv;
{
    register int cnt;
    register struct sprrec *tmpspr;
    int i, dir;      /* During sprite creation, direction it enters from */
    UWORD clxdat;    /* Value of collision register */
    ULONG msgclass;  /* Message class from Intuition message */
    struct IntuiMessage *msg, *GetMsg();
    struct Task *thistask, *FindTask ();

    /* First set the priority of this task. */

    if (thistask = FindTask (NULL)) {
      if (argc < 2 || (cnt = atoi (argv[1])) < -128 || cnt > 127)
        cnt = DEFPRIORITY;
      SetTaskPri (thistask, (long)cnt);
    }

    OpenStuff ();

    InitSprites ();

    /* Detect collisions between sprite 0 and sprites 2, 4, 6 */

    cstm->clxcon = 0xF000;

    sprcnt = 0; score = 0;

    while (1) {

	/* If the game has ended, the simply display the score and wait */
        /* for a message... */

	if (sprcnt >= MAXSPRITES) {
	  ChgSpriteHeight (0);
	  for (cnt = 0; cnt < NUMSPR; cnt++) spr[cnt].mode = SPRITEINACTIVE;
	  ShowScore ();
	  Delay (NEWGAMEDELAY);
	  sprcnt = 0; score = 0;
        }

	while (msg = GetMsg (win->UserPort)) {
	  msgclass = msg->Class;
 	  ReplyMsg (msg);
	  switch (msgclass) {
            case CLOSEWINDOW: CloseStuff (0); 
	    case ACTIVEWINDOW: 
              if (!spritesactive) ChgSpriteHeight (SPRHEIGHT);
              spritesactive = TRUE;
              break;
            case INACTIVEWINDOW:
	      if (spritesactive) ChgSpriteHeight (0);
	      spritesactive = FALSE;
	      break;
            default: ;
          }
        }

	if (!spritesactive) Wait (1L << win->UserPort->mp_SigBit);

        else {
	 
         for (cnt = 0; cnt < NUMSPR; cnt++)  {

          tmpspr = &spr[cnt];

          switch (tmpspr->mode) {

           case SPRITEINACTIVE:
            if (tmpspr->code++ > 0 && sprcnt < MAXSPRITES) {
	      if (cnt < 2) sprcnt++;	/* One more sprite gone... */
	      dir = (RndNum(2) ? 1 : -1);
              tmpspr->mode = SPRITEACTIVE;
              tmpspr->code = 0;
              tmpspr->vx   = dir * (10 + RndNum (12) + 6 * (cnt != 0));
              tmpspr->vy   = (3 - RndNum(7));
              tmpspr->ax   = dir * (RndNum(6) - 2) * RndNum (2);
              tmpspr->ay   = (-1 + RndNum(3)) * cnt;
              tmpspr->px   = ((dir == 1) ? (XMIN + 1) : (XMAX - 1));
              tmpspr->py   = ((tmpspr->vy > 0) ? 10 : 100) + RndNum(91);
	      tmpspr->actspr.height = SPRHEIGHT;
            };
            break;

	   case SPRITEHIT:
            if (--(tmpspr->code) == 0) {      /* Sprite now inactive */
              tmpspr->actspr.height = 0;
	      tmpspr->mode = SPRITEINACTIVE;
              tmpspr->code = - RndNum(20);
	      DoColors ();
            } else 
              SetRGB4 (vp, (long)(21 + (cnt << 2) + (tmpspr->code >> 3)),
                       0L, 0L, 0L);  /* Black! (as the sprite dies...) */
            break;
              
           case SPRITEACTIVE:
	    /* The following two lines depend on the fact that ">>" works */
            /* correctly on signed quantities here. */
            tmpspr->px += (tmpspr->vx >> 2);
            tmpspr->py += (tmpspr->vy >> 3);  
	    tmpspr->vx += tmpspr->ax;
	    tmpspr->vy += tmpspr->ay;
            if (tmpspr->px < XMIN || tmpspr->px > XMAX ||
                tmpspr->py > YMAX) {
              tmpspr->actspr.height = 0;
	      tmpspr->mode = SPRITEINACTIVE;
              tmpspr->code = - RndNum(20);
	      DoColors ();
            } else if (tmpspr->py < YMIN) {
              tmpspr->py = 0;
              tmpspr->vy = (-tmpspr->vy) * (1 + RndNum(2));
            };
	    switch (RndNum(128)) {
             case 0: tmpspr->ax = -(tmpspr->ax * (RndNum(2) + 1));
	     case 1: tmpspr->ay = -tmpspr->ay; break;
	     case 2: tmpspr->ay++;
             case 3: tmpspr->ax++; break;
             default: ;
	    }
            break;

          default: ;
    
         }

        }

	for (cnt = 0; cnt < NUMSPR; cnt++) 
          MoveSprite (vp, &(spr[cnt].actspr), 
                      (long)spr[cnt].px, (long)spr[cnt].py);

        clxdat = cstm->clxdat;

	for (cnt = 0; cnt < NUMSPR; cnt++) 
	  if ((clxdat & colmask[cnt]) && (spr[cnt].mode == SPRITEACTIVE)) {
            spr[cnt].mode = SPRITEHIT;
            spr[cnt].code = 24;
	    if (cnt == 2) {   /* Hit a negative one, set color to gray */
 	      score -= 1;
	      for (i = 0; i < 3; i++)
                SetRGB4(vp, (long)(21 + (cnt << 2) + i), 10L, 10L, 10L);
            } else {
 	      score += 1;
	      for (i = 0; i < 3; i++)
                SetRGB4(vp, (long)(21 + (cnt << 2) + i), 15L, 14L, 0L);
	    }
          }

        WaitTOF ();

        if (--rotatecount < 0) {
          coloroffset = mod3[coloroffset + 1];
          DoColors ();
          rotatecount = ROTATECOUNT;
        }

       }      
    }
}		


ChgSpriteHeight (newheight)
int newheight;
{
  register int cnt;

  for (cnt = 0; cnt < NUMSPR; cnt++) {
    spr[cnt].actspr.height = newheight;
    ChangeSprite (vp, &(spr[cnt].actspr), spr[cnt].actspr.posctldata);
  }
}  


OpenStuff ()
{
	register int i;

	if (!(IntuitionBase = OpenLibrary ("intuition.library", 0))) 
          CloseStuff (1);
	if (!(GfxBase = OpenLibrary ("graphics.library", 0))) 
          CloseStuff (1);
	if (!(win = OpenWindow (&newwindow))) 
          CloseStuff (1);

	vp = ViewPortAddress (win);

        spritesactive = FALSE;
	coloroffset = 0;
        rotatecount = ROTATECOUNT;
}

CloseStuff (exitcode)
int exitcode;
{
	register int cnt;

	for (cnt = 0; cnt < NUMSPR; cnt++) 
	  if (spr[cnt].actspr.num) FreeSprite ((long) spr[cnt].actspr.num);
	if (sprbuf)
  	  FreeMem (sprbuf, (long)((SPRHEIGHT * 2 + 2) * NUMSPR));
	if (win) CloseWindow (win);
	if (GfxBase) CloseLibrary (GfxBase);
	if (IntuitionBase) CloseLibrary (IntuitionBase);
	exit (exitcode);
}


/* This routine mostly from Leo Schwab. It obtains the actual hardware */
/* sprites, and the chip memory to put the sprite images in, and the */
/* copies the "ballimage" into the sprite area... */

InitSprites ()
{
	UWORD *cw, *curspr, *ball;
	int cnt, line;

        /* We use 3 sprites, 0..2, corresponding to system sprites 2,4,6 */

	if (!(sprbuf = 
            AllocMem ((long)((SPRHEIGHT * 2 + 4) * NUMSPR * 2), MEMF_CHIP)))
          CloseStuff (1);

	cw = sprbuf;

	for (cnt = 0; cnt < NUMSPR; cnt++) {
          if (GetSprite (&(spr[cnt].actspr), (long) ((cnt << 1) + 2)) == -1)
            CloseStuff (1);
          spr[cnt].mode = SPRITEINACTIVE;
	  spr[cnt].code = -ROTATECOUNT;
          spr[cnt].actspr.height = 0;
	  ball = ballimage;
 	  curspr = cw;
  	  *cw++ = 0; *cw++ = 0;
	  for (line = 0; line < SPRHEIGHT * 2; line++) *c
w++ = *ball++;
	  *cw++ = 0; *cw++ = 0;
          ChangeSprite (vp, &(spr[cnt].actspr), curspr);
	}
}

/* DoColors will rotate the colors for the 3 sprites. Rotation consists */
/* of putting color 2 into 1, 3 into 2, and 1 into 3. The values are */
/* hardwired below, so we don't actually go in and obtain the values */
/* from the window... */

DoColors ()
{
  register int cnt, creg;
  
  for (cnt = 0; cnt < NUMSPR-1; cnt++) 
    if (spr[cnt].mode != SPRITEHIT) {
      creg = 21 + (cnt << 2);
      SetRGB4 (vp, (long)(creg + coloroffset), 15L, 0L, 0L);
      SetRGB4 (vp, (long)(creg + mod3[coloroffset+1]), 15L, 1L, 1L);
      SetRGB4 (vp, (long)(creg + mod3[coloroffset+2]), 15L, 14L, 13L);
    }      
  if (spr[2].mode != SPRITEHIT) {
    SetRGB4 (vp, (long)(29 + coloroffset), 2L, 12L, 0L);
    SetRGB4 (vp, (long)(29 + mod3[coloroffset+1]), 0L, 11L, 1L);
    SetRGB4 (vp, (long)(29 + mod3[coloroffset+2]), 15L, 14L, 13L);
  }      
}

ShowScore ()
{
  register UBYTE *loc = &scorestr[7];

  strcpy (scorestr, "SCORE:     ");
  if (score < 0) {*loc++ = '-'; score = -score;};
  if (score > 99) *loc++ = (score / 100) + '0';
  if (score > 9)  *loc++ = (score / 10) % 10 + '0';
  *loc++ = (score % 10) + '0';
  *loc = 0;
  SetWindowTitles (win, scorestr, -1L);
  DisplayBeep (NULL);   /* You might want to get rid of this!!! */
  WindowToFront (win);
}  
  
ULONG seed;

/* Random number generator, translated into C from rnd.s */
/* provided with Oing! Returns an integer 0..max-1. Don't know how */
/* well it works, but the game seems random enough. */

RndNum (max)
UWORD max;
{
  int result = (int)(seed % max);
  long datevec[3], *DateStamp();
  
  if (seed == 0L) {
    DateStamp (datevec);
    seed = (UWORD) (datevec[1] * datevec[2]);
  } else {
    if (seed > 0x80000000L) seed = ((seed << 1) ^ 0x1D872B41L);
    else seed <<= 1;
  }

  return (result);
}

/* YaBoing, by Ali T. Ozer */

dillon@CORY.BERKELEY.EDU (Matt Dillon) (09/23/86)

	Interesting, there is a line in the program (somewhere in 400-500)

	  for (line = 0; line < SPRHEIGHT * 2; line++) *c
w++ = *ball++;


	that I guess works fine on Manx, but Lattice dies on.  Specifcally,
the fact that the 'cw' has been split in the middle by a newline.  There were
also a couple other small things lattice gave errors for (mainly pointer
conversion warnings... one variable which was declared and not used, etc...)
But after about 2 minutes debugging time the program compiled.

	Does Manx report those types of errors?  (Hey! maybe this is
something Lettuce is good at for once!)

	Thanks for posting it!  It really is fun to fool around with.  I 
especially like the window .. er, lack of a window.  And, as you said, it's
a great educator for sprites.

						-Matt

mwm@eris.berkeley.edu (Mike Meyer) (09/23/86)

Cute. I like it.

After a couple a minutes play, I decided I like the negative game
(catch the green ball, miss the red ones) better. It even scores that
correctly :-)

As for compilers, I don't think Manx catches those. I constantly find
myself taking unused variables out of code written for the Manx
compiler.

Didn't you notice this stuff right off, Matt? Put in function
prototypes, and it catches mistyped parameters, too. If only my Unix
compilers were that reasonable.


	<mike

ali@navajo.STANFORD.EDU (Ali Ozer) (09/23/86)

[]

In article <1328@jade.BERKELEY.EDU> mwm@eris.UUCP (Mike Meyer) writes:
>After a couple a minutes play, I decided I like the negative game
>(catch the green ball, miss the red ones) better. It even scores that
>correctly :-)

Yes, in fact, playing the negative game you have no upper (or shall we
say lower) bound on the score. Playing normally, your high score is limited
to 100 (as only 100 red balls get thrown in). Of the 3 sprites, one is always
assigned to green and the other two red... Thus, you should normally get
50 green balls, with an effective score of -50. BUT, if you're quick enough
and kill green balls as soon as they appear, you can get a lower score.

One question --- I was playing with YaBoing at priority set to various
values, and even crazy ones like -100 and +100. At -100, of course, any
other task pretty much can do whatever it wants... BUT, at +100, we were
doing a diskcopy in the back, and diskcopy failed with "unrecoverable error
in source disk."  Is it possible that because a task was running with a
priority higher than anything else in the system diskcopy actuall screwed
up and also killed the source disk? (Otherwise, running YaBoing at its
default priority, I had no problems using diskcopy or any other task in the
background. In fact, running YaBoing at priority = 100, I was able to do a lot
of things, even disk related, like "list," etc...)

Ali Ozer

keithe@tekgvs.UUCP (Keith Ericson) (09/26/86)

OK OK OK - Just WHAT do I have to do to get the Yaboing to
compile without errors - or warnings - under lattice (3.03
I think). I feel SOOOOoooo stupid!

keith