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