/************************************* TODO Work on an explosion routine which replaces dead sprites with the appropriate explosion animation and takes the explosions off the screen once their animation is complete. The sprites are going to move around randomly, so add an x_incr and y_incr variable to the sprites, this can also be used to do sprites which bounce around the screen. What about the BIG 'S' !!!! i.e. Sound ?? *************************************/ #include #include #include "pad.h" #define SpriteAdd (0x800c0000) // Address of sprite page #define DodecAdd (0x80110000) // Address of sprite page #define CharacterAdd (0x800e0000) // Address of character page #define BackgroundAdd (0x800f8000) // Address of background page #define OT_LEN 8 #define MAXSPRITES 200 #define MAXCHARS 200 PACKET packet[2][100*sizeof(GsSPRITE)]; GsIMAGE spr_tim_data; // Bitmaps for sprites GsIMAGE chr_tim_data; // Bitmaps for characters GsIMAGE bak_tim_data; // Bitmaps for background GsIMAGE Shd_tim_data; // Bitmaps for Hyperspace Sheild GsOT ot[2]; GsOT_TAG oth[2][1<thesprite.x - 29, spr_ptr->thesprite.y + 2, 15, 0, 0); // Add the players exhaust animation AddSprite(7, 200, 120, 100, 0, 0); AddSprite(11, 0, 0, 0, -2, 0); // Top Row AddSprite(11, 83, 0, 0, -2, 0); AddSprite(11, 166, 0, 0, -2, 0); AddSprite(11, 249, 0, 0, -2, 0); AddSprite(11, 332, 0, 0, -2, 0); AddSprite(12, 0, 212, 0, -2, 0); // Bottom Row AddSprite(12, 83, 212, 0, -2, 0); AddSprite(12, 166, 212, 0, -2, 0); AddSprite(12, 249, 212, 0, -2, 0); AddSprite(12, 332, 212, 0, -2, 0); AddSprite(10, 0, 0, 0, -1, 0); // Big Wall Background AddSprite(10, 128, 0, 0, -1, 0); AddSprite(10, 256, 0, 0, -1, 0); AddSprite(10, 384, 0, 0, -1, 0); AddSprite(10, 0, 128, 0, -1, 0); AddSprite(10, 128, 128, 0, -1, 0); AddSprite(10, 256, 128, 0, -1, 0); AddSprite(10, 384, 128, 0, -1, 0); AddSprite(10, 0, 256, 0, -1, 0); AddSprite(10, 128, 256, 0, -1, 0); AddSprite(10, 256, 256, 0, -1, 0); AddSprite(10, 384, 256, 0, -1, 0); // AddSprite(11, 0, 0, 0, 0, 0); // AddSprite(12, 50, 50, 0, 0, 0); while(PLAYING) { GetButtons(); UpdateSprites(); DrawAll(); AddRandom(); } return 3; // Causes the program to crash, but is quicker than a clean termination } void DrawAll() { int i; int activebuff = GsGetActiveBuff(); GsSetWorkBase((PACKET *)packet[activebuff]); GsClearOt(0,0,&ot[activebuff]); for(i = 0; i < numchars; i++) GsSortFastSprite(char_ptr + i, &ot[activebuff],0); // Put text on to screen for(i = 0; i < thetable.numsprites; i++) { GsSortFastSprite(&(spr_ptr + i)->thesprite, &ot[activebuff], (spr_ptr + i)->pri); // Put sprites on to screen } DrawSync(0); VSync(0); GsSwapDispBuff(); GsSortClear(0, 0, 0, &ot[activebuff]); GsDrawOt(&ot[activebuff]); } void InitGraph() { int i; SetVideoMode(MODE_PAL); GsInitGraph(320, 240, 4, 0, 0); GsDefDispBuff(0, 0, 0, 240); for(i = 0; i < 2; i++) { ot[i].length = OT_LEN; ot[i].org = oth[i]; } } // This routine adds a sprite to the sprite table (speed refers to speed of animation) void AddSprite(int type, int x, int y, int speed, int xinc, int yinc) { if(thetable.numsprites > MAXSPRITES - 1) return; // Don't exceed array bounds (spr_ptr + thetable.numsprites)->type = type; (spr_ptr + thetable.numsprites)->thesprite.x = x; (spr_ptr + thetable.numsprites)->thesprite.y = y; (spr_ptr + thetable.numsprites)->thesprite.w = *(array_ptr + type * ax + 1); (spr_ptr + thetable.numsprites)->thesprite.h = *(array_ptr + type * ax + 2); (spr_ptr + thetable.numsprites)->thesprite.u = *(array_ptr + type * ax + 3); (spr_ptr + thetable.numsprites)->thesprite.v = *(array_ptr + type * ax + 4); (spr_ptr + thetable.numsprites)->thesprite.r = 128; (spr_ptr + thetable.numsprites)->thesprite.g = 128; (spr_ptr + thetable.numsprites)->thesprite.b = 128; (spr_ptr + thetable.numsprites)->thesprite.attribute = 0x01000000; switch(type) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: (spr_ptr + thetable.numsprites)->thesprite.tpage = GetTPage(1,0,spr_tim_data.px, spr_tim_data.py); (spr_ptr + thetable.numsprites)->thesprite.cx = spr_tim_data.cx; (spr_ptr + thetable.numsprites)->thesprite.cy = spr_tim_data.cy; break; case 7: (spr_ptr + thetable.numsprites)->thesprite.tpage = GetTPage(1,0,Shd_tim_data.px, Shd_tim_data.py); (spr_ptr + thetable.numsprites)->thesprite.cx = Shd_tim_data.cx; (spr_ptr + thetable.numsprites)->thesprite.cy = Shd_tim_data.cy; break; case 10: case 11: case 12: (spr_ptr + thetable.numsprites)->thesprite.tpage = GetTPage(1,0,bak_tim_data.px, bak_tim_data.py); (spr_ptr + thetable.numsprites)->thesprite.cx = bak_tim_data.cx; (spr_ptr + thetable.numsprites)->thesprite.cy = bak_tim_data.cy; break; } (spr_ptr + thetable.numsprites)->incr = speed; (spr_ptr + thetable.numsprites)->kill = 0; (spr_ptr + thetable.numsprites)->x_incr = xinc; (spr_ptr + thetable.numsprites)->y_incr = yinc; (spr_ptr + thetable.numsprites)->pri = 2; switch(type) { case 1: case 0: (spr_ptr + thetable.numsprites)->pri = 1; // Player and exhaust break; case 7: case 2: (spr_ptr + thetable.numsprites)->currentframe = rand() % 8; // A Mine (spr_ptr + thetable.numsprites)->strength = 5; break; case 4: (spr_ptr + thetable.numsprites)->strength = 1; // A Bullet break; case 6: (spr_ptr + thetable.numsprites)->strength = 1; // A Little Red Ball break; case 10: (spr_ptr + thetable.numsprites)->pri = 5; break; case 11: case 12: (spr_ptr + thetable.numsprites)->pri = 0; break; } thetable.numsprites++; } void UpdateSprites() { int i; static int t; char buffer[20]; Sprite *spriteptr; if(thetable.numsprites > t) t = thetable.numsprites; sprintf(buffer, "Now = %ld, Max = %ld", thetable.numsprites, t); TextPrint(buffer, 20, 5); collision(); for(i = 0; i < thetable.numsprites; i++) // Work through the whole table { spriteptr = (spr_ptr + i); // point at the sprite we are dealing with at the moment if(spriteptr->type == 4 && spriteptr->thesprite.x > 305) // If it is a player bullet { spriteptr->kill = 1; // If it has reched RHS kill it } if(spriteptr->kill == 1) { KillSprite(i); continue; // we're done with this sprite now } if((spriteptr->type == 10) && (spriteptr->thesprite.x < -128)) // Wraparound the background spriteptr->thesprite.x = 384; if(((spriteptr->type == 11) || (spriteptr->type == 12)) && (spriteptr->thesprite.x < -83)) // Wraparound the background spriteptr->thesprite.x = 332; /* Bounce Routine */ spriteptr->thesprite.x += spriteptr->x_incr; spriteptr->thesprite.y += spriteptr->y_incr; // Red balls are destroyed on leaving screen if(spriteptr->thesprite.x > 319 - spriteptr->thesprite.w || spriteptr->thesprite.x < 0) { if(spriteptr->type == 6) spriteptr->kill = 1; else if(spriteptr->type < 10) spriteptr->x_incr = -spriteptr->x_incr; } if(spriteptr->thesprite.y > 240 - spriteptr->thesprite.h || spriteptr->thesprite.y < 0) { if(spriteptr->type == 6) spriteptr->kill = 1; else if(spriteptr->type < 10) spriteptr->y_incr = -spriteptr->y_incr; } if(*(array_ptr + spriteptr->type * ax) == 1) continue; //if only one frame, start with next sprite spriteptr->animcount += spriteptr->incr; // increase the 'animcount' variable by the 'incr' variable if(spriteptr->animcount >= 100) // if 'animcount' has reached 100 { spriteptr->animcount = 0; // Reset the 'animcount' variable spriteptr->currentframe ++; // Increment the frame counter if(spriteptr->currentframe >= *(array_ptr + spriteptr->type * ax))// If we have gone past the last frame { spriteptr->currentframe = 0; // go back to the first frame if(spriteptr->type == 5) // If it is an explosion sprite, we kill it after the last frame spriteptr->kill = 1; } } // move 'u' and 'v' offsets to correct frame in sprite page. spriteptr->thesprite.u = *(array_ptr + (spriteptr->type * ax) + 3 + (2 * spriteptr->currentframe)); spriteptr->thesprite.v = *(array_ptr + (spriteptr->type * ax) + 4 + (2 * spriteptr->currentframe)); } } // Loads the sprite page in to offscreen video memory void LoadSpritePage(u_long address, GsIMAGE *tim_data) { RECT rect; // TIM info starts 4 bytes after start of file GsGetTimInfo ((u_long*)(address + 4),tim_data); rect.x = tim_data->px; // setup structure members rect.y = tim_data->py; rect.w = tim_data->pw; rect.h = tim_data->ph; LoadImage(&rect, tim_data->pixel); // load sprite page into frame buffer rect.x = tim_data->cx; // setup structure members rect.y = tim_data->cy; rect.w = tim_data->cw; rect.h = tim_data->ch; LoadImage(&rect, tim_data->clut); // load Colour Look Up Table into frame buffer DrawSync(0); // Wait for LoadImage() to finish before returning } void GetButtons() { static int f; if(f < 100) f += 30; PADstatus=PadRead(); if(PADstatus & PADselect)PLAYING=0; if((PADstatus & PADLleft) && (thetable.sprites->thesprite.x > 2)) { thetable.sprites->thesprite.x -= 2; // Move ship (thetable.sprites + 1)->thesprite.x -= 2; // Move exhaust } if((PADstatus & PADLright) && (thetable.sprites->thesprite.x < 280)) { thetable.sprites->thesprite.x += 2; (thetable.sprites + 1)->thesprite.x += 2; } if((PADstatus & PADLup) && (thetable.sprites->thesprite.y > 2)) { thetable.sprites->thesprite.y -= 2; (thetable.sprites + 1)->thesprite.y -= 2; } if((PADstatus & PADLdown) && (thetable.sprites->thesprite.y < 220)) { thetable.sprites->thesprite.y += 2; (thetable.sprites + 1)->thesprite.y += 2; } if(PADstatus & PADRdown && f > 99) { fire(); f = 0; } } // This function is used to remove sprites from the table. // Suppose we wish to remove the fourth sprite in the table, // we first copy the top sprite from the table in to the fourth // position and then decrement the 'thetable.numsprites' variable. void KillSprite(int num) { spr_ptr = thetable.sprites; thetable.numsprites--; if(num <= thetable.numsprites) { (spr_ptr + num)->animcount = (spr_ptr + thetable.numsprites)->animcount; (spr_ptr + num)->currentframe= (spr_ptr + thetable.numsprites)->currentframe; (spr_ptr + num)->kill = 0; (spr_ptr + num)->incr = (spr_ptr + thetable.numsprites)->incr; (spr_ptr + num)->type = (spr_ptr + thetable.numsprites)->type; (spr_ptr + num)->thesprite.attribute = (spr_ptr + thetable.numsprites)->thesprite.attribute; (spr_ptr + num)->thesprite.x = (spr_ptr + thetable.numsprites)->thesprite.x; (spr_ptr + num)->thesprite.y = (spr_ptr + thetable.numsprites)->thesprite.y; (spr_ptr + num)->thesprite.w = (spr_ptr + thetable.numsprites)->thesprite.w; (spr_ptr + num)->thesprite.h = (spr_ptr + thetable.numsprites)->thesprite.h; (spr_ptr + num)->thesprite.tpage = (spr_ptr + thetable.numsprites)->thesprite.tpage; (spr_ptr + num)->thesprite.u = (spr_ptr + thetable.numsprites)->thesprite.u; (spr_ptr + num)->thesprite.v = (spr_ptr + thetable.numsprites)->thesprite.v; (spr_ptr + num)->thesprite.cx = (spr_ptr + thetable.numsprites)->thesprite.cx; (spr_ptr + num)->thesprite.cy = (spr_ptr + thetable.numsprites)->thesprite.cy; (spr_ptr + num)->thesprite.r = (spr_ptr + thetable.numsprites)->thesprite.r; (spr_ptr + num)->thesprite.g = (spr_ptr + thetable.numsprites)->thesprite.g; (spr_ptr + num)->thesprite.b = (spr_ptr + thetable.numsprites)->thesprite.b; (spr_ptr + num)->thesprite.mx = (spr_ptr + thetable.numsprites)->thesprite.mx; (spr_ptr + num)->thesprite.my = (spr_ptr + thetable.numsprites)->thesprite.my; (spr_ptr + num)->thesprite.scalex = (spr_ptr + thetable.numsprites)->thesprite.scalex; (spr_ptr + num)->thesprite.scaley = (spr_ptr + thetable.numsprites)->thesprite.scaley; (spr_ptr + num)->thesprite.rotate = (spr_ptr + thetable.numsprites)->thesprite.rotate; (spr_ptr + num)->x_incr = (spr_ptr + thetable.numsprites)->x_incr; (spr_ptr + num)->y_incr = (spr_ptr + thetable.numsprites)->y_incr; (spr_ptr + num)->pri = (spr_ptr + thetable.numsprites)->pri; } } void fire() { AddSprite(4, spr_ptr->thesprite.x + 20, spr_ptr->thesprite.y + 10, 0, 5, 0); } void TextPrint(char *message, int x, int y) { int xpos = x; char *m_ptr = message; numchars = 0; while(*m_ptr) { (char_ptr + numchars)->x = xpos; (char_ptr + numchars)->y = y; (char_ptr + numchars)->w = 12; (char_ptr + numchars)->h = 22; (char_ptr + numchars)->u = ((*m_ptr - 32) % 21) * 12 + 1; (char_ptr + numchars)->v = ((*m_ptr - 32) / 21) * 22 + 1; (char_ptr + numchars)->r = 128; (char_ptr + numchars)->g = 128; (char_ptr + numchars)->b = 128; (char_ptr + numchars)->cx = chr_tim_data.cx; (char_ptr + numchars)->cy = chr_tim_data.cy; (char_ptr + numchars)->attribute = 0x01000000; (char_ptr + numchars)->tpage = GetTPage(1,0,chr_tim_data.px, chr_tim_data.py); xpos += 12; numchars++; m_ptr++; } } // This routine goes through the whole table looking for // collisions between player and enemy, and bullets and enemy void collision() { int i, j; Sprite *spriteptr1, *spriteptr2; // start with the first sprite and, if it is // a bullet, check to see if its second x coord is within // any other sprites first and second x coords, if it is, // check to see if its central y coord is within any other sprites // upper and lower y coords, if it is, write hit to the screen. // We are just going to deal with bullets (type 4) at the moment for(i = 0; i < thetable.numsprites; i++) { spriteptr1 = (spr_ptr + i); // point at the sprite we are dealing with at the moment if(spriteptr1->type == 4) { for(j = 0; j < thetable.numsprites; j++) { spriteptr2 = (spr_ptr + j); // point at the sprite we are testing against at the moment if(spriteptr2->type > 9) continue; // sprite types greater than 10 are background if((spriteptr1->thesprite.x + spriteptr1->thesprite.w >= spriteptr2->thesprite.x) && (spriteptr1->thesprite.x + spriteptr1->thesprite.w <= spriteptr2->thesprite.x + spriteptr2->thesprite.w)) { if((spriteptr1->thesprite.y + spriteptr1->thesprite.h / 2 >= spriteptr2->thesprite.y) && (spriteptr1->thesprite.y + spriteptr1->thesprite.h / 2 <= spriteptr2->thesprite.y + spriteptr2->thesprite.h) && (spriteptr2->type != 4) && (spriteptr2->type != 0) && (spriteptr2->type != 1) && (spriteptr2->type != 5)) { if(spriteptr2->type == 2) // If the sprite is one of the mines { spriteptr2->type = 5; // Change it to an explosion spriteptr2->thesprite.h = 17; spriteptr2->thesprite.w = 23; spriteptr2->incr = 20; spriteptr2->currentframe = 0; // Add the six red balls AddSprite(6, spriteptr2->thesprite.x, spriteptr2->thesprite.y, 0, (rand() % 8) - 4, (rand() % 8) - 4); AddSprite(6, spriteptr2->thesprite.x + 15, spriteptr2->thesprite.y - 5, 0, (rand() % 8) - 4, (rand() % 8) - 4); AddSprite(6, spriteptr2->thesprite.x + 30, spriteptr2->thesprite.y, 0, (rand() % 8) - 4, (rand() % 8) - 4); AddSprite(6, spriteptr2->thesprite.x + 30, spriteptr2->thesprite.y + 30, 0, (rand() % 8) - 4, (rand() % 8) - 4); AddSprite(6, spriteptr2->thesprite.x + 15, spriteptr2->thesprite.y - 35, 0, (rand() % 8) - 4, (rand() % 8) - 4); AddSprite(6, spriteptr2->thesprite.x, spriteptr2->thesprite.y + 30, 0, (rand() % 8) - 4, (rand() % 8) - 4); } else KillSprite(j); KillSprite(i); } } } } } } void AddRandom() { if((rand() % 100 > 95 || thetable.numsprites < 30) && (thetable.numsprites < 39)) AddSprite(2, 280, rand() % 210, 25, (rand() % 8) - 4, (rand() % 8) - 4); }