/* * PONG! - Nick Slaven 21st April 1998 * written in 2.5 days * * www.netyaroze-europe.com/~nslaven/ * NickSlaven@Compuserve.com * */ #include #include "pad.h" //debug flag //#define DEBUG // display stuff with the all important Ordered Table & Packet Area #define OT_LENGTH 1 #define MAXPACKETS 25000 /* mysterious, just think of a number and double it! */ GsOT WorldOT[2]; GsOT_TAG OTTags[2][1<>16):(_r)))) #define PADexit (PADstart|PADselect) // Controller input buffers volatile u_char *bb0, *bb1; // current padvalue u_long PadValue; int Pad1v, Pad2v; // game definition #define BAT_W 8 #define BAT_H 32 #define BALL_WH 8 #define COLOUR 0x80 #define BALL_SPEED 5 #define BALL_FAST 10 #define GAME_WIN_SCORE 5 typedef struct tagBALL { GsBOXF Bx; // box for ball int Vx,Vy; // x & y velocity of ball int InServe; // Flag for ball being served or in play int Server; // index of player serving } BALL; typedef struct tagPLAYER { GsBOXF Bat; int Score; int SlamOn; // is "circle" button pressed int SlamCnt; // timing of "circle" button press } PLAYER; BALL Ball; PLAYER Player[2]; GsBOXF Court[2]; int GameOn=0; // function prototypes u_long PadRead(void); void PadAction(void); void InitGFX(void); void InitWorld(void); void CollisionDetect(void); void UpdateBall(void); void DispScore(void); void DispDigit(int,int,char); void PlayTune(void); void DispTitle(void); void GameInit(void); inline u_long PadRead() { // Get analog pad (vertical) positions Pad1v = *(bb0+7) - (BAT_H>>1); Pad2v = *(bb1+7) - (BAT_H>>1); return(~(*(bb0+3) | *(bb0+2) << 8 | *(bb1+3) << 16 | *(bb1+2) << 24)); } inline int BoxColl(GsBOXF * b1,GsBOXF *b2) { int dx,dy; dx=b2->x-b1->x; dy=b2->y-b1->y; if((dxw)&&(dx>-b2->w)&&(dyh)&&(dy>-b2->h)) return 1; else return 0; } // ------------------------------------- MAIN int main(void) { int i,cnt; InitGFX(); InitWorld(); VabId=SsVabTransfer(SND_VH,SND_VB,-1,1); while (!IsPAD(0,PadValue=PadRead(),PADexit)) { activeBuff = GsGetActiveBuff(); // Get working video buffer index GsSetWorkBase((PACKET *)GpuPacketArea[activeBuff]); // set working area accordingly GsClearOt(0, 0, &WorldOT[activeBuff]); // clear working ordered table area if(GameOn) { CollisionDetect(); PadAction(); UpdateBall(); } else { DispTitle(); if(IsPAD(0,PadValue,PADstart)) GameInit(); } GsSortBoxFill(&Ball.Bx,&WorldOT[activeBuff], 0); // place the "rectangle" primitive for the ball, .. for(i=0;i<2;i++) { GsSortBoxFill(&Player[i].Bat,&WorldOT[activeBuff], 0); // .. the bats GsSortBoxFill(&Court[i] ,&WorldOT[activeBuff], 0); // .. and the Court, } // into the working ordered table DrawSync(0); // make sure we have drawn all the stuff from // the ordered table we filled last time round the // while loop (see GsDrawOt below) cnt = VSync(0); // wait for vertical blank GsSwapDispBuff(); // switch display and working video buffers GsSortClear(0, 0, 0, &WorldOT[activeBuff]); // place the "clear screen" primitive in the // working ordered table GsDrawOt(&WorldOT[activeBuff]); // finally tell GPU to draw working ordered table // contents on working video buffer // remember this is a parallel process, and // items at same level in ordered table are drawn // in reverse placing order #ifdef DEBUG FntPrint("VSync:%d\n",cnt); FntPrint("X :%d\tY :%d\nvX:%d\tvY:%d\n",Ball.Bx.x,Ball.Bx.y,Ball.Vx,Ball.Vy); FntFlush(-1); #endif } SsVabClose(VabId); ResetGraph(3); return(0); } void PadAction(void) { int i,x; // Set bats to analog position if ( (*(bb0+1) >> 4) == 0x7 ) // Analog pad Player[0].Bat.y = Pad1v; else // Normal pad { if(IsPAD(0,PadValue,PADRdown)) x=10; else x=1; if(IsPAD(0,PadValue,PADLup )) Player[0].Bat.y-=x; if(IsPAD(0,PadValue,PADLdown)) Player[0].Bat.y+=x; } if ( (*(bb1+1) >> 4) == 0x7 ) // Analog pad Player[1].Bat.y = Pad2v; else // Normal pad { if(IsPAD(1,PadValue,PADRdown)) x=10; else x=1; if(IsPAD(1,PadValue,PADLup )) Player[1].Bat.y-=x; if(IsPAD(1,PadValue,PADLdown)) Player[1].Bat.y+=x; } for(i=0;i<2;i++) { if(!IsPAD(i,PadValue,PADRright)) Player[i].SlamOn=0; if(!IsPAD(i,PadValue,PADRright)) Player[i].SlamOn=0; if(Player[i].SlamOn && Player[i].SlamCnt) --Player[i].SlamCnt; if(IsPAD(i,PadValue,PADRright)&&!Player[i].SlamOn) { Player[i].SlamOn =1; Player[i].SlamCnt=3; } #ifdef DEBUG if(i==1) Player[i].Bat.y=Ball.Bx.y+BALL_WH/2-BAT_H/2; #endif limitRange(Player[i].Bat.y,0,FrameY-BAT_H); } } void GameInit(void) { int i; GameOn=1; for(i=0;i<2;i++) { Player[i].Score =0; Player[i].Bat.y =(FrameY-BAT_H)/2; Player[i].SlamOn =0; Player[i].SlamCnt=0; } } void CollisionDetect(void) { int i,dist,v; for(i=0;i<2;i++) { // ball hits bat ? if(BoxColl(&Player[i].Bat,&Ball.Bx)) { if(Player[i].SlamOn && Player[i].SlamCnt) { if(i) SsUtKeyOn(VabId,0,0,72,0, 0,127); else SsUtKeyOn(VabId,0,0,69,0,127, 0); v=BALL_FAST; } else v=BALL_SPEED; Ball.Vx=i?-v:v; dist=Ball.Bx.y-Player[i].Bat.y-(BAT_H-BALL_WH)/2; Ball.Vy=dist/2; if(i) SsUtKeyOn(VabId,0,0,60,0, 0,127); else SsUtKeyOn(VabId,0,0,57,0,127, 0); } // ball off left or right edges, then player has scored !!! if( ((i==0)&&(Ball.Bx.x>=FRAME_X-BALL_WH/2)) || ((i==1)&&(Ball.Bx.x<=-BALL_WH/2 )) ) { Player[i].Score++; if(Player[i].Score==GAME_WIN_SCORE) GameOn=0; Ball.Server=i; Ball.InServe=1; PlayTune(); #ifdef DEBUG Ball.Server=0; #endif } // ball hits court walls ? if(BoxColl(&Court[i],&Ball.Bx)) { Ball.Vy=-Ball.Vy; SsUtKeyOn(VabId,0,0,53,0,127,127); } } } void PlayTune(void) { static short Note[]={48,52,55,57,58,48,52,55,57,58,48,52,55,57,58}; short i,j,voice; for(i=0;iD[y][x]) { SBox.x=XStart+x*TXC_WH; SBox.y=YStart+y*TXC_WH; GsSortBoxFill(&SBox,&WorldOT[activeBuff], 0); } } } }