#include #include "balltex.h" #include "pad.h" #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 256 #define BACK_ADDR0 0x80090000 #define BACK_ADDR1 0x80099000 #define BACK_ADDR2 0x80102000 #define BAT_ADDR 0x8010A000 #define WALL_ADDR 0x8010C000 #define SHAT_ADDR 0x8010D000 #define BALL_COLOUR 1 #define MEM 0x80110000 #define ZMAX 100 #define SPEED 1 #define BAT_Z 5 #define LimitRange(x, l, h) ((x)=((x)<(l)?(l):(x)>(h)?(h):(x))) struct BALLMOVE { float px; // position before offset float py; float pz; float mx; // movement in direction x, y, z float my; float mz; }; GsOT WorldOT[2]; GsOT_TAG OTTags[2][1<<3]; PACKET GpuPacketArea[2][240000]; GsSPRITE bat, ball, brick[12][10], back0, back1, back2, shatter[100]; GsBOXF dead; char bricks[12][10], shat_used; int output_buffer_index; u_char PrevMode; int fnt_id[8]; short lives, hits; short done, death; struct BALLMOVE ballMove; extern DISPENV GsDISPENV; void InitGame(void); void InitSprites(void); void InitSprite( GsSPRITE *sprite, u_long attribute, short x, short y, u_short w, u_short h, u_short tpage, u_char u, u_char v, short cx, short cy, u_char r, u_char g, u_char b, short mx, short my, short scalex, short scaley, long rotate ); void MoveBall(void); void MoveBat(void); void Freeze(void); void Position_Ball(void); GsIMAGE *ReadTIM( u_long *addr ); void PrintFonts(void); void Ball_Offset(void); void UpdateScreen(short died); main() { done = 0; PadInit(); InitGame(); InitSprites(); while(done == 0) { MoveBat(); MoveBall(); PrintFonts(); UpdateScreen(0); } return(0); } // ******************************************************************************************* // INIT GAME // ******************************************************************************************* void InitGame(void) { int count, a, b; for (a = 0; a < 12; a++) { for (b = 0; b < 10; b++) { bricks[a][b] = 1; } } FntLoad(960,256); fnt_id[0] = FntOpen(0, 10, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 80); PrevMode = GetVideoMode(); SetVideoMode( MODE_PAL ); ResetGraph(0); GsInitGraph( SCREEN_WIDTH, SCREEN_HEIGHT, GsOFSGPU|GsNONINTER, 0, 0 ); GsDefDispBuff( 0, 0, 0, SCREEN_HEIGHT ); GsDISPENV.screen.x=6; GsDISPENV.screen.y=20; GsDISPENV.screen.h=256; for( count = 0; count < 2; count++ ) { WorldOT[count].length = 3; WorldOT[count].org = OTTags[count]; } GsClearOt( 0, 0, &WorldOT[output_buffer_index] ); GsClearOt( 0, 0, &WorldOT[output_buffer_index + 1] ); lives = 5; hits = 0; death = 0; } // ******************************************************************************************* // INIT SPRITES // ******************************************************************************************* void InitSprites(void) { GsIMAGE *tim; u_short tpage; int a, b; RECT rect; // Init Background tim = ReadTIM( (u_long *)BACK_ADDR0); tpage = GetTPage( 1, 0, tim->px, tim->py); InitSprite( &back0, (1<<24), 0, 0, 128, 256, tpage, 0, 0, tim->cx, tim->cy, 0x80, 0x80, 0x80, 0, 0, ONE, ONE, 0 ); tim = ReadTIM( (u_long *)BACK_ADDR1); tpage = GetTPage( 1, 0, tim->px, tim->py); InitSprite( &back1, (1<<24), 128, 0, 128, 256, tpage, 0, 0, tim->cx, tim->cy, 0x80, 0x80, 0x80, 0, 0, ONE, ONE, 0 ); tim = ReadTIM( (u_long *)BACK_ADDR2); tpage = GetTPage( 1, 0, tim->px, tim->py); InitSprite( &back2, (1<<24), 256, 0, 64, 256, tpage, 0, 0, tim->cx, tim->cy, 0x80, 0x80, 0x80, 0, 0, ONE, ONE, 0 ); // Init Bat tim = ReadTIM( (u_long *)BAT_ADDR); tpage = GetTPage( 1, 0, tim->px, tim->py ); InitSprite( &bat, (1<<24) | (1<<30), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 64, 64, tpage, 0, 0, tim->cx, tim->cy, 0x40, 0x40, 0x40, 32, 32, ONE, ONE, 0 ); // Init Bricks tim = ReadTIM( (u_long *)WALL_ADDR); tpage = GetTPage( 1, 0, tim->px, tim->py ); for(a = 0; a < 12; a++) { for(b = 0; b < 10; b++) { InitSprite( &brick[a][b], (1<<24), 65 + a * 16, 48 + b * 16, 16, 16, tpage, 0, 128, tim->cx, tim->cy, 0x80, 0x80, 0x80, 0, 0, ONE, ONE, 0 ); } } // Load Data For The Ball rect.x = 576; rect.y = 0; rect.w = 16/4; rect.h = 16; LoadImage(&rect, ball16x16); tpage = GetTPage(0, 0, 576, 0); rect.x = 320; rect.y = 266; rect.w = 256; rect.h = 1; LoadImage(&rect, ballcolor[BALL_COLOUR]); // Initialise The Ball Sprite InitSprite( &ball, 0, 0, 0, 16, 16, tpage, 0, 0, 320, 266, 0x80, 0x80, 0x80, 8, 8, ONE, ONE, 0 ); ballMove.px = SCREEN_WIDTH / 2; ballMove.py = SCREEN_HEIGHT / 2; ballMove.pz = BAT_Z; ballMove.mx = 0; ballMove.my = 0; ballMove.mz = 1; // Load Data For The Shattery bits tim = ReadTIM( (u_long *)SHAT_ADDR); tpage = GetTPage( 1, 0, tim->px, tim->py); // Initialise the Shattery bits for (a=0; a<100; a++) InitSprite( &shatter[a], (1<<24), 0, 0, 64, 64, tpage, 0, 64, tim->cx, tim->cy, 0x80, 0x80, 0x80, 0, 0, ONE, ONE, 0); shat_used = 0; // Init dead attributes (box used for red fading on death) dead.attribute = (1<<29) | (1<<30); } // ******************************************************************************************* // READ TIM FILES // ******************************************************************************************* GsIMAGE *ReadTIM( u_long *addr ) { static GsIMAGE tim; RECT rect; // skip id and initialize image structure addr ++; GsGetTimInfo(addr, &tim); DrawSync(0); // transfer pixel data to VRAM rect.x = tim.px; rect.y = tim.py; rect.w = tim.pw; rect.h = tim.ph; LoadImage(&rect, tim.pixel); DrawSync(0); // check if CLUT exists and transfer it to VRAM if( (tim.pmode >> 3) & 0x01 ) { rect.x = tim.cx; rect.y = tim.cy; rect.w = tim.cw; rect.h = tim.ch; LoadImage(&rect, tim.clut); } DrawSync(0); return(&tim); } // ******************************************************************************************* // INIT SPRITE // ******************************************************************************************* void InitSprite( GsSPRITE *sprite, u_long attribute, short x, short y, u_short w, u_short h, u_short tpage, u_char u, u_char v, short cx, short cy, u_char r, u_char g, u_char b, short mx, short my, short scalex, short scaley, long rotate ) { sprite->attribute = attribute; sprite->x = x; sprite->y = y; sprite->w = w; sprite->h = h; sprite->tpage = tpage; sprite->u = u; sprite->v = v; sprite->cx = cx; sprite->cy = cy; sprite->r = r; sprite->g = g; sprite->b = b; sprite->mx = mx; sprite->my = my; sprite->scalex = scalex; sprite->scaley = scaley; sprite->rotate = rotate; } // ******************************************************************************************* // MOVE BAT // ******************************************************************************************* void MoveBat(void) { short x, y, s; u_long padd = PadRead(); x = bat.x; y = bat.y; s = SPEED * 2; if ( padd & PADR1 ) s = SPEED; if ( padd & PADLup ) { y -= (3 * s); LimitRange(y, 0, SCREEN_HEIGHT); } if ( padd & PADLdown ) { y += (3 * s); LimitRange(y, 0, SCREEN_HEIGHT); } if ( padd & PADLleft ) { x -= (3 * s); LimitRange(x, 0, SCREEN_WIDTH); } if ( padd & PADLright ) { x += (3 * s); LimitRange(x, 0, SCREEN_WIDTH); } if ( padd & PADselect ) done = 1; if ( padd & PADL1 ) while ( PadRead() & PADL1 ); bat.x = x; bat.y = y; } // ******************************************************************************************* // FREEZE (for death) // ******************************************************************************************* void Freeze(void) { while (! (PadRead() & PADR2) ); lives --; death = 2; } // ******************************************************************************************* // POSITION BALL ON BAT // ******************************************************************************************* void Position_Ball(void) { char ok = 0; short x, y, x1, y1, s; u_long padd; s = SPEED * 2; while (ok == 0) { padd = PadRead(); x = bat.x; y = bat.y; x1 = ballMove.px; y1 = ballMove.py; if ( padd & PADLup ) { y -= (3 * s); LimitRange(y, 0, SCREEN_HEIGHT - 1); if (y > 0) { y1 -= (3 * s); LimitRange(y1, y - 32, y + 32); } } if ( padd & PADLdown ) { y += (3 * s); LimitRange(y, 0, SCREEN_HEIGHT - 1); if (y < SCREEN_HEIGHT - 1) { y1 += (3 * s); LimitRange(y1, y - 32, y + 32); } } if ( padd & PADLleft ) { x -= (3 * s); LimitRange(x, 0, SCREEN_WIDTH - 1); if (x > 0) { x1 -= (3 * s); LimitRange(x1, x - 32, x + 32); } } if ( padd & PADLright ) { x += (3 * s); LimitRange(x, 0, SCREEN_WIDTH - 1); if (x < SCREEN_WIDTH - 1) { x1 += (3 * s); LimitRange(x1, x - 32, x + 32); } } if ( padd & PADRup ) { y1 -= 3; LimitRange(y1, y - 32, y + 32); } if ( padd & PADRdown ) { y1 += 3; LimitRange(y1, y - 32, y + 32); } if ( padd & PADRleft ) { x1 -= 3; LimitRange(x1, x - 32, x + 32); } if ( padd & PADRright ) { x1 += 3; LimitRange(x1, x - 32, x + 32); } bat.x = x; bat.y = y; ballMove.px = x1; ballMove.py = y1; ballMove.mx = ((ballMove.px - bat.x) / 6); ballMove.my = ((ballMove.py - bat.y) / 6); Ball_Offset(); if ( padd & PADR1 ) { ok = 1; } PrintFonts(); UpdateScreen(0); } death = 0; } // ******************************************************************************************* // MOVE BALL // ******************************************************************************************* void MoveBall(void) { short rebound, a; RECT rect1, rect2; // Move Ball In Appropriate Direction ballMove.px += (ballMove.mx * SPEED); ballMove.py += (ballMove.my * SPEED); ballMove.pz += (ballMove.mz * SPEED); if( ballMove.px > (SCREEN_WIDTH - 1) || ballMove.px < 0 ) { ballMove.mx = 0 - ballMove.mx; } if( ballMove.py > (SCREEN_HEIGHT - 1) || ballMove.py < 0 ) { ballMove.my = 0 - ballMove.my; } // Check if ball hits bricks if( ballMove.pz == ZMAX ) { if ( bricks[ (int)((ballMove.px / (320 / 12)) - 0.5) ][ (int)((ballMove.py / (SCREEN_HEIGHT / 10)) - 0.5) ] ) { bricks[ (int)((ballMove.px / (320 / 12)) - 0.5) ][ (int)((ballMove.py / (SCREEN_HEIGHT / 10)) - 0.5) ] = 0; brick[ (int)((ballMove.px / (320 / 12)) - 0.5) ][ (int)((ballMove.py / (SCREEN_HEIGHT / 10)) - 0.5) ].h = 0; hits ++; } ballMove.mz = 0 - ballMove.mz; } // Check if ball hits bat if (ballMove.pz == BAT_Z ) { if ( ( abs( (ballMove.px - bat.x) * (ballMove.px - bat.x) ) + abs( (ballMove.py - bat.y) * (ballMove.py - bat.y) ) ) < 1444) { ballMove.mz = 0 - ballMove.mz; ballMove.mx += ((ballMove.px - bat.x) / 6); ballMove.my += ((ballMove.py - bat.y) / 6); } } // If ball has missed bat if ( ballMove.pz == 0 ) { rebound = 1; if (shat_used > 0) { for (a = 0; a < shat_used; a++) { if ( ballMove.px > shatter[a].x && ballMove.px < (shatter[a].x + 63) && ballMove.py > shatter[a].y && ballMove.py < (shatter[a].y + 63) ) rebound = 0; } } if (rebound == 0) { PrintFonts(); UpdateScreen(0); PrintFonts(); UpdateScreen(0); for (a = 0; a < 256; a += 8) { dead.x = 0; dead.y = 0; dead.w = SCREEN_WIDTH; dead.h = SCREEN_HEIGHT; dead.r = 0; dead.g = dead.b = a; PrintFonts(); UpdateScreen(1); } death = 1; PrintFonts(); UpdateScreen(1); Freeze(); bat.x = ballMove.px = 160; bat.y = ballMove.py = 120; ballMove.pz = BAT_Z; ballMove.mx = 0; ballMove.my = 0; ballMove.mz = 1; shat_used = 0; Position_Ball(); } if (rebound == 1) { shatter[shat_used].x = ballMove.px - 32; shatter[shat_used].y = ballMove.py - 32; shat_used += 1; ballMove.mz = 0 - ballMove.mz; } } Ball_Offset(); } // ******************************************************************************************* // BALL OFFSET // ******************************************************************************************* void Ball_Offset(void) { short x1, x2, x3, y1, y2, y3; x1 = (int)ballMove.px; x2 = (((int)ballMove.px << 9) / ((1 << 9) * 5 / 3)) + 64; x3 = x1 - x2; y1 = (int)ballMove.py; y2 = (((int)ballMove.py << 9) / ((1 << 9) * 5 / 3)) + 48; y3 = y1 - y2; ball.x = (int)(x2 + (x3 * (ZMAX - ballMove.pz) / 100)); ball.y = (int)(y2 + (y3 * (ZMAX - ballMove.pz) / 100)); ball.scalex = ball.scaley = ONE - ballMove.pz * 40 / SPEED; } // ******************************************************************************************* // PRINT LIVES AND SCORE // ******************************************************************************************* void PrintFonts(void) { switch (death) { case 0: FntPrint(fnt_id[0], "~c888Lives: %d\tHits: %d", lives, hits); break; case 1: FntPrint(fnt_id[0], "~c888Lives: %d\tHits: %d\tPress R2", lives, hits); break; case 2: FntPrint(fnt_id[0], "~c888Lives: %d\tHits: %d\tR1 to FIRE", lives, hits); break; } } // ******************************************************************************************* // UPDATE SCREEN // ******************************************************************************************* void UpdateScreen(short died) { GsSPRITE *brk; char a, b; output_buffer_index = GsGetActiveBuff(); GsSetWorkBase((PACKET*)GpuPacketArea[output_buffer_index]); GsClearOt(0, 0, &WorldOT[output_buffer_index]); if (shat_used > 0) { for (a = 0; a < shat_used; a++) { GsSortSprite(&shatter[a], &WorldOT[output_buffer_index], 0); } } // Put items into OT GsSortSprite(&back0, &WorldOT[output_buffer_index], 5); GsSortSprite(&back1, &WorldOT[output_buffer_index], 5); GsSortSprite(&back2, &WorldOT[output_buffer_index], 5); if (shat_used > 0) { for (a = 0; a < shat_used; a++) { GsSortSprite(&shatter[a], &WorldOT[output_buffer_index], 1); } } if (ballMove.pz < BAT_Z) { GsSortSprite(&ball, &WorldOT[output_buffer_index], 2); GsSortSprite(&bat, &WorldOT[output_buffer_index], 3); } else { GsSortSprite(&bat, &WorldOT[output_buffer_index], 2); GsSortSprite(&ball, &WorldOT[output_buffer_index], 3); } for (a = 0; a < 12; a++) { for (b = 0; b < 10; b++) { GsSortSprite(&brick[a][b], &WorldOT[output_buffer_index], 4); } } if (died) GsSortBoxFill(&dead, &WorldOT[output_buffer_index], 0); DrawSync(0); VSync(0); FntFlush(fnt_id[0]); GsSwapDispBuff(); GsSortClear(0, 0, 0, &WorldOT[output_buffer_index]); GsDrawOt(&WorldOT[output_buffer_index]); }