/* pong.c The classic game of pong in Megamax C for the Mac. Thanks to MacTutor (Vol 1, No. 5 April 1985 page 39) for animation techniques. If you are reading this and don't subscribe to MacTutor, consider it. No resource file is needed. This program, source and object, is in the public domain and not for sale. Author : David L. O'Connor, 370 Eden St. Buffalo, N.Y. 14220. (716) 828-0898. CIS - 70265,1172 Date : July, 1985 Version 2 */ #include #include #include #include #include #include #include #include #include #include #include /* the game diRections */ #define STOPPED 0 #define UP 1 #define DOWN 2 #define LEFT 3 #define RIGHT 4 #define UP_LEFT 5 #define UP_RIGHT 6 #define DOWN_LEFT 7 #define DOWN_RIGHT 8 /* paddle + ball dimensions */ #define PADWIDTH 10 #define PADLENGTH 45 #define PADINSET 10 #define BALLWIDTH 9 #define BALLLENGTH 9 #define BALLSPEED 7 #define PADDLESPEED 9 #define HIGHSCORE 21 /* the menu ids */ #define appleid 128 #define fileid 129 #define editid 130 #define skillid 131 #define soundid 132 /* from the MAC's standard pattern list */ #define PAD_PAT ((*pat_Handle)->pat_list[6]) #define WALL_PAT ((*pat_Handle)->pat_list[10]) typedef struct{ int pat_cnt; Pattern pat_list[38]; } sys_patterns; typedef struct { Rect r; int dir; int speed; int score; } paddle; typedef struct { RgnHandle Rgn; RgnHandle oldRgn; RgnHandle unRgn; int dir; int speed; int on; } target; typedef struct { int mode; Tone triplet[1]; } bleep_tag; typedef struct { int mode; Tone triplet[2]; } blat_tag; static bleep_tag bleep_buf; static blat_tag blat_buf; static paddle l_paddle, r_paddle; static target ball; static sys_patterns **pat_Handle; static WindowPtr gamewindow, which_window; static WindowRecord winstorage; static Rect r, dragRect, top_wall, bottom_wall; static EventRecord gameEvent; static MenuHandle gamemenu[5]; static char menutitle[1]; static int skill_level, done, paused, last_won, volleys, sound_on; static char title[] = { 65, ' ',' ',' ',' ',' ',' ',' ',' ', 'L','e','f','t',' ',' ','0','0', ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ', 'M','A','C','_','P','o','n','g', ' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ', 'R','i','g','h','t',' ','0','0', ' ',' ',' ',' ',' ',' ',' ',' ', '\0'}; main() { setup(); while (! done){ Handle_Events(); play_pong(); } FlushEvents(everyEvent, 0); StopSound(); ExitToShell(); } setup() { done = 0; skill_level = 2; sound_on = 1; last_won = RIGHT; InitGraf(&thePort); InitFonts(); InitWindows(); TEInit(); InitDialogs(0); InitCursor(); InitSounds(); pat_Handle = (sys_patterns**) GetResource('PAT#', 0); FlushEvents(everyEvent, 0); SetRect(&r, 4, 40, 508, 338); SetRect(&dragRect, 4, 24, r.right - 4, r.bottom - 4); gamewindow = NewWindow(&winstorage, &r, title, 1, 0L, -1L, 0, 0L); SetPort(gamewindow); build_menus(); ShowCursor(); create_l_paddle(); create_r_paddle(); create_walls(); create_ball(); Init_game(); } /* pretty much straight from SAMP in I.M. */ Handle_Events() { SystemTask(); if (GetNextEvent(everyEvent, &gameEvent)){ switch (gameEvent.what){ case mouseDown : switch (FindWindow(gameEvent.where, &which_window)){ case inMenuBar : DoCommand(MenuSelect(gameEvent.where)); break; case inSysWindow : SystemClick(&gameEvent, which_window); break; case inDrag : DragWindow(which_window, gameEvent.where, &dragRect); break; case inContent : if (which_window != FrontWindow()) SelectWindow(which_window); break; } break; case updateEvt : SetPort(gamewindow); BeginUpdate(gamewindow); FillRect(&l_paddle.r, PAD_PAT); FillRect(&r_paddle.r, PAD_PAT); FillRect(&top_wall, WALL_PAT); FillRect(&bottom_wall, WALL_PAT); if (ball.on) PaintRgn(ball.Rgn); EndUpdate(gamewindow); break; } } } play_pong() { if ( (! paused) && (l_paddle.score < HIGHSCORE && r_paddle.score < HIGHSCORE)){ if (! ball.on) serve_ball(); check_status(); move_left_paddle(); move_right_paddle(); move_ball(); } } InitSounds(){ bleep_buf.mode = swMode; bleep_buf.triplet[0].count = 1000; bleep_buf.triplet[0].amplitude = 255; bleep_buf.triplet[0].duration = 5; blat_buf.mode = swMode; blat_buf.triplet[0].count = 1000; blat_buf.triplet[0].amplitude = 255; blat_buf.triplet[0].duration = 5; blat_buf.triplet[1].count = 3000; blat_buf.triplet[1].amplitude = 255; blat_buf.triplet[1].duration = 10; } build_menus() { register int i; InitMenus(); gamemenu[0] = NewMenu(appleid, "\p"); gamemenu[1] = NewMenu(fileid, "\pFile"); gamemenu[2] = NewMenu(editid, "\pEdit"); gamemenu[3] = NewMenu(skillid, "\pSkill"); gamemenu[4] = NewMenu(soundid, "\pSound"); AddResMenu(gamemenu[0],'DRVR'); AppendMenu(gamemenu[1],"\pPause;Restart;Quit"); AppendMenu(gamemenu[2],"\p(Undo;(-;(Cut;(Copy;(Paste;(Clear"); AppendMenu(gamemenu[3],"\pBeginner;Novice;Normal;Expert"); AppendMenu(gamemenu[4],"\pSound Off"); for(i = 0; i < 5; i++) InsertMenu(gamemenu[i], 0); CheckItem(gamemenu[3], skill_level, 1); DrawMenuBar(); } disable_edit_menu() { DisableItem(gamemenu[2], 1); DisableItem(gamemenu[2], 3); DisableItem(gamemenu[2], 4); DisableItem(gamemenu[2], 5); DisableItem(gamemenu[2], 6); } enable_edit_menu() { EnableItem(gamemenu[2], 1); EnableItem(gamemenu[2], 3); EnableItem(gamemenu[2], 4); EnableItem(gamemenu[2], 5); EnableItem(gamemenu[2], 6); } DoCommand(menu_selection) long menu_selection; { register int the_item; static char name[256]; the_item = LoWord(menu_selection); switch (HiWord(menu_selection)){ case appleid : GetItem(gamemenu[0], the_item, name); OpenDeskAcc(name); SetPort(gamewindow); break; case editid: SystemEdit(the_item - 1); break; case fileid: switch (the_item){ case 1 : if (paused){ paused = 0; SetItem(gamemenu[1], 1, "\pPause"); } else{ paused = 1; SetItem(gamemenu[1], 1, "\pContinue"); } break; case 2: Init_game(); break; case 3 : done = 1; break; } break; case skillid : CheckItem(gamemenu[3], skill_level, 0); skill_level = the_item; CheckItem(gamemenu[3], skill_level, 1); break; case soundid: if (sound_on){ sound_on = 0; SetItem(gamemenu[4], 1, "\pSound On"); } else{ sound_on = 1; SetItem(gamemenu[4], 1, "\pSound Off"); } break; } HiliteMenu(0); } create_l_paddle() { l_paddle.dir = STOPPED; l_paddle.speed = PADDLESPEED; l_paddle.score = 0; SetRect (&l_paddle.r, winstorage.port.portRect.left + PADINSET, winstorage.port.portRect.top + PADINSET, winstorage.port.portRect.left + PADINSET + PADWIDTH, winstorage.port.portRect.top + PADINSET + PADLENGTH); FillRect(&l_paddle.r, PAD_PAT); } create_r_paddle() { r_paddle.dir = STOPPED; r_paddle.speed = PADDLESPEED; r_paddle.score = 0; SetRect (&r_paddle.r, winstorage.port.portRect.right - PADWIDTH - PADINSET, winstorage.port.portRect.top + PADINSET, winstorage.port.portRect.right - PADWIDTH - PADINSET + PADWIDTH, winstorage.port.portRect.top + PADINSET + PADLENGTH); FillRect(&r_paddle.r, PAD_PAT); } create_walls() { SetRect(&top_wall, winstorage.port.portRect.left + 20, winstorage.port.portRect.top + 5, winstorage.port.portRect.right - 20, winstorage.port.portRect.top + 20); FillRect(&top_wall, WALL_PAT); SetRect(&bottom_wall, winstorage.port.portRect.left + 20, winstorage.port.portRect.bottom - 20, winstorage.port.portRect.right - 20, winstorage.port.portRect.bottom - 5); FillRect(&bottom_wall, WALL_PAT); } create_ball() { ball.Rgn = NewRgn(); ball.oldRgn = NewRgn(); ball.unRgn = NewRgn(); ball.dir = LEFT; ball.speed = BALLSPEED; SetRect (&r, 250, 150, 250 + BALLWIDTH, 150 + BALLLENGTH); OpenRgn(); FrameOval(&r); CloseRgn(ball.Rgn); } serve_ball() { register i; OffsetRgn(ball.Rgn, 250 - ((**ball.Rgn).rgnBBox.right), 150 - ((**ball.Rgn).rgnBBox.top) ); for (i = 0; i < 250; i++){ check_status(); move_right_paddle(); move_left_paddle(); move_ball(); } ball.dir = (last_won == RIGHT) ? LEFT : RIGHT; ball.speed = BALLSPEED; ball.on = 1; PaintRgn(ball.Rgn); bleep(); } /* someone scored a point */ kill_ball() { ball.on = volleys = 0; CopyRgn(ball.Rgn, ball.unRgn); EraseRgn(ball.Rgn); recover_from_collision(); blat(); display_score(); } Init_game() { l_paddle.score = r_paddle.score = 0; ball.speed = BALLSPEED; kill_ball(); } /* check for bounces, diRection changes, scoring, etc */ check_status() { static Rect *ball_r; register ball_top = (**ball.Rgn).rgnBBox.top; register ball_bottom = (**ball.Rgn).rgnBBox.bottom; register ball_left = (**ball.Rgn).rgnBBox.left; register ball_right = (**ball.Rgn).rgnBBox.right; ball_r = &((**ball.Rgn).rgnBBox); /* make it a little harder as time goes by */ if (volleys > 35) ball.speed = BALLSPEED + 6; else if (volleys > 30) ball.speed = BALLSPEED + 5; else if (volleys > 25) ball.speed = BALLSPEED + 4; else if (volleys > 20) ball.speed = BALLSPEED + 3; else if (volleys > 15) ball.speed = BALLSPEED + 2; else if (volleys > 10) ball.speed = BALLSPEED + 1; r_paddle.speed = ball.speed + 2; /* the right paddle tries to track the ball */ if ( (ball_right > 250) && (ball.dir == UP_RIGHT || ball.dir == DOWN_RIGHT || ball.dir == RIGHT) ){ if (ball_top + handicap() < r_paddle.r.top) r_paddle.dir = UP; else if (ball_bottom - handicap() > r_paddle.r.bottom) r_paddle.dir = DOWN; else r_paddle.dir = STOPPED; } else r_paddle.dir = STOPPED; /* the ball and the left boundry */ if (ball_left < l_paddle.r.right ){ if (SectRect(ball_r, &l_paddle.r, &r)){ volleys++; bleep(); if (ball_top <= l_paddle.r.top + 15) ball.dir = UP_RIGHT; else if (ball_top > l_paddle.r.top + 15 && ball_bottom < l_paddle.r.top + 30) ball.dir = RIGHT; else ball.dir = DOWN_RIGHT; } else{ last_won = RIGHT; r_paddle.score++; kill_ball(); } return; } /* the ball and the right boundry */ if (ball_right > r_paddle.r.left){ if (SectRect(ball_r, &r_paddle.r, &r)){ volleys++; bleep(); if (ball_top <= r_paddle.r.top + 15) ball.dir = UP_LEFT; else if (ball_top > r_paddle.r.top + 15 && ball_bottom < r_paddle.r.top + 30) ball.dir = LEFT; else ball.dir = DOWN_LEFT; } else{ last_won = LEFT; l_paddle.score++; kill_ball(); } return; } /* the ball and the top wall */ if (ball_top < top_wall.bottom){ if (ball.dir == UP_LEFT) ball.dir = DOWN_LEFT; else if (ball.dir == UP_RIGHT) ball.dir = DOWN_RIGHT; bleep(); return; } /* the ball and the bottom wall */ if (ball_bottom > bottom_wall.top){ if (ball.dir == DOWN_LEFT) ball.dir = UP_LEFT; else if (ball.dir == DOWN_RIGHT) ball.dir = UP_RIGHT; bleep(); return; } } /* the ball eats the walls and paddles */ recover_from_collision() { register Rect *rp = &((**ball.unRgn).rgnBBox); if (SectRect(rp, &top_wall, &r)) FillRect(&r, WALL_PAT); else if (SectRect(rp, &bottom_wall, &r)) FillRect(&r, WALL_PAT); if (SectRect(rp, &l_paddle.r, &r)) FillRect(&r, PAD_PAT); else if (SectRect(rp, &r_paddle.r, &r)) FillRect(&r, PAD_PAT); } move_left_paddle() { static Point mouseloc; register int newtop, newbottom; GetMouse(&mouseloc); if (mouseloc.v != l_paddle.r.top){ r.left = l_paddle.r.left; r.right = l_paddle.r.right; if (mouseloc.v <= winstorage.port.portRect.top){ newtop = winstorage.port.portRect.top; newbottom = newtop + PADLENGTH; } else if (mouseloc.v + PADLENGTH >= winstorage.port.portRect.bottom){ newbottom = winstorage.port.portRect.bottom; newtop = newbottom - PADLENGTH; } else{ newtop = mouseloc.v; newbottom = newtop + PADLENGTH; } if (newtop > l_paddle.r.top){ r.top = l_paddle.r.top; r.bottom = (newtop > l_paddle.r.bottom) ? l_paddle.r.bottom : newtop; } else if (newtop < l_paddle.r.top){ r.bottom = l_paddle.r.bottom; r.top = (newbottom < l_paddle.r.top) ? l_paddle.r.top : newbottom; } l_paddle.r.top = newtop; l_paddle.r.bottom = newbottom; EraseRect(&r); FillRect(&l_paddle.r, PAD_PAT); } else FillRect(&l_paddle.r, PAD_PAT); } move_right_paddle() { if (r_paddle.dir == STOPPED) FillRect(&r_paddle.r, PAD_PAT); else{ r.left = r_paddle.r.left; r.right = r_paddle.r.right; switch (r_paddle.dir){ case UP: r.bottom = r_paddle.r.bottom; r_paddle.r.top -= r_paddle.speed; r_paddle.r.bottom -= r_paddle.speed; r.top = r_paddle.r.bottom; break; case DOWN: r.top = r_paddle.r.top; r_paddle.r.top += r_paddle.speed; r_paddle.r.bottom += r_paddle.speed; r.bottom = r_paddle.r.top; break; } EraseRect(&r); FillRect(&r_paddle.r, PAD_PAT); } } move_ball() { if (ball.on){ CopyRgn(ball.Rgn, ball.oldRgn); switch (ball.dir){ case LEFT: OffsetRgn(ball.Rgn, -ball.speed, 0); break; case RIGHT: OffsetRgn(ball.Rgn, ball.speed, 0); break; case UP_LEFT: OffsetRgn(ball.Rgn, -ball.speed, -ball.speed); break; case UP_RIGHT: OffsetRgn(ball.Rgn, ball.speed, -ball.speed); break; case DOWN_LEFT: OffsetRgn(ball.Rgn, -ball.speed, ball.speed); break; case DOWN_RIGHT: OffsetRgn(ball.Rgn, ball.speed, ball.speed); break; } UnionRgn(ball.Rgn, ball.oldRgn, ball.unRgn); DiffRgn(ball.unRgn, ball.Rgn, ball.unRgn); EraseRgn(ball.unRgn); PaintRgn(ball.Rgn); recover_from_collision(); } } display_score() { static long i; i = l_paddle.score; title[15]=0x30+(i/100); title[16]=0x30+((i%100)/10); title[17]=0x30+(i%10); i = r_paddle.score; title[63]=0x30+(i/100); title[64]=0x30+((i%100)/10); title[65]=0x30+(i%10); SetWTitle(gamewindow, title); } bleep() { if (sound_on){ if (! SoundDone()) StopSound(); StartSound((Ptr) &bleep_buf, (long) sizeof(bleep_buf), (Ptr) 0); } } blat() { if (sound_on){ if (! SoundDone()) StopSound(); StartSound((Ptr) &blat_buf, (long) sizeof(blat_buf), (Ptr) 0); } } /* Every so often, let the Mac's paddle fail to track the ball until the ball has passed it by a certain amount. This is the heart of a satisfying game. */ int handicap() { register mac_skill; switch(skill_level){ case 1 : mac_skill = 2; break; case 2 : mac_skill = 8; break; case 3 : mac_skill = 27; break; case 4 : mac_skill = 64; break; default : mac_skill = 2; break; } return ((Random() % mac_skill) == 0) ? 5 : 0; }