// Connect-4 v1.0 written by James Shaughnessy // // Connect-4 is Copyright (c) MB Games // // Just fancied writing it having played that hyouge version you // see in a number of pubs (Hey you might think playing a six-foot // wide Connect-4 with massive wooden discs is sad but I know a // place that had Ker-Plunk with knitting needles and ping-pong balls..) // // Don't get bored too quickly :) // // Cheers! // Jim 3rd July 1998 // ----------------------------------------- // James Shaughnessy james@manc.u-net.com // http://www.netyaroze-europe.com/~shaughnj // ----------------------------------------- #include #include #include "pad.h" // Debug flag //#define DEBUG #define VH_ADDR (0x80090000) #define VB_ADDR (0x80091820) #define CHIPS_ADDR (0x800a0000) #define LEVER_ADDR (0x800a0680) #define STAND_ADDR (0x800a0c10) #define OT_LENGTH (1) #define NUM_SPRITES (256) #define PLAYER1 (1) #define PLAYER2 (2) #define FALSE (0) #define TRUE (1) #define INTRO (0) #define DROPCHIP (1) #define COLUMNCHANGE (2) #define DROPPING (3) #define SHOWWINNER (4) #define RELEASECHIPS (5) #define INSTRUCTIONS (6) // Global Variables u_long timer = 0; int exit_game = FALSE; // Exit flag int mode = INSTRUCTIONS; // Game mode int player = PLAYER1; int winner = NULL; int chiptr = 0; // Chips in play int row = 0, column = 0; int fallamount = 0; // Drop speed int still_falling = FALSE; // Used in release chips int activeBuff; int hsyncs; u_long padStatus, padStatus2; extern DISPENV GsDISPENV; // Sound vars short vab_id; short hit_sound; short volume; // Board matrix 0 = empty // 1 = player1's chip // 2 = player2's chip char board[8][7]; char wincell[8][7]; // Sprite pointer array GsSPRITE *pboardchip[8][7]; // Chips and rack sprites GsSPRITE chip[56]; GsSPRITE BOARD[8][7]; GsSPRITE lever; GsSPRITE stand[2]; // Background colour gradation GsGLINE gline = {0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 0}; // Two Ordering Tables, one for each buffer GsOT WorldOT[2]; // Two Ordering Table Tags, one for each buffer GsOT_TAG OTTag[2][1< 7) column = 0; } while (board[column][0]); mode = COLUMNCHANGE; } // Drop chip in current column else if (padStatus2 & PADcross) { // Find lowest available row while (row < 6 && !board[column][row+1]) row++; fallamount = 1; // Set hit sound and volume hit_sound = 1+rand()%5; volume = 31+(row<<4); // Dropping sound SsUtKeyOn(vab_id, 0, 0, 48, 0, 127, 127); mode = DROPPING; } break; case COLUMNCHANGE : { int difference = chip[chiptr].x - (25+34*column); if (abs(difference) > 2) chip[chiptr].x -= (difference>>1); else { if (difference > 0) chip[chiptr].x--; else if (difference < 0) chip[chiptr].x++; else mode = DROPCHIP; } } break; case DROPPING : // Drop chip chip[chiptr].y += fallamount++; // Fallen to lowest position if (chip[chiptr].y > (16+32*row)) { chip[chiptr].y = (16+32*row); // Set board matrix board[column][row] = player; // Set sprite pointer pboardchip[column][row] = &chip[chiptr]; if ((winner = TestBoard()) || ++chiptr == 56) { SsUtAllKeyOff(0); // Hit sound SsUtKeyOn(vab_id, hit_sound, 0, 48, 0, volume, volume); mode = SHOWWINNER; } else { // Change player if (player == PLAYER1) player = PLAYER2; else player = PLAYER1; row = 0; // Find next available column if necessary while (board[column][0]) { if (++column > 7) column = 0; } chip[chiptr].x = 25+34*column; chip[chiptr].y = 0; SsUtAllKeyOff(0); // Hit sound SsUtKeyOn(vab_id, hit_sound, 0, 48, 0, volume, volume); mode = DROPCHIP; } } break; case SHOWWINNER : for (j = 0; j < 7; j++) { for (i = 0; i < 8; i++) { if ( wincell[i][j] ) pboardchip[i][j]->r = pboardchip[i][j]->g = pboardchip[i][j]->b = abs(((timer%33)<<3)-128); } } break; case RELEASECHIPS : still_falling = FALSE; for (i = 0; i <= chiptr; i++) { if ((chip[i].y += fallamount) < 256) still_falling = TRUE; } fallamount++; if (!still_falling) { lever.r = lever.g = lever.b = 128; lever.y++; mode = INTRO; } break; case INSTRUCTIONS : FntPrint("Connect 4 (c) MB Games\n\n"); FntPrint("PlayStation version v1.0\nby James Shaughnessy\n\n"); FntPrint("Player 1 RED\nPlayer 2 YELLOW\n\n"); FntPrint("Cross to drop\nTriangle to release rack\n\n"); FntPrint("Press START to go first\n\n"); if (padStatus & (PADstart | (PADstart<<16))) mode = INTRO; break; default : break; } } // Tests board for a connect-4 and sets wincell array for winning cells int TestBoard() { int i, j, chip, win = 0; for (j=0; j < 7; j++) { for (i=0; i < 8; i++) { chip = board[i][j]; if (chip) { if ( (i < 5) && (chip == board[i+1][j] && chip == board[i+2][j] && chip == board[i+3][j]) ) win = wincell[i][j] = wincell[i+1][j] = wincell[i+2][j] = wincell[i+3][j] = chip; if ( ((i < 5) && (j < 4)) && (chip == board[i+1][j+1] && chip == board[i+2][j+2] && chip == board[i+3][j+3]) ) win = wincell[i][j] = wincell[i+1][j+1] = wincell[i+2][j+2] = wincell[i+3][j+3] = chip; if ( (j < 4) && (chip == board[i][j+1] && chip == board[i][j+2] && chip == board[i][j+3]) ) win = wincell[i][j] = wincell[i][j+1] = wincell[i][j+2] = wincell[i][j+3] = chip; if ( ((i > 2) && (j < 4)) && (chip == board[i-1][j+1] && chip == board[i-2][j+2] && chip == board[i-3][j+3]) ) win = wincell[i][j] = wincell[i-1][j+1] = wincell[i-2][j+2] = wincell[i-3][j+3] = chip; } } } return win; } // Reset board matrix void ResetBoard() { int i, j; // Reset sprites to normal brightness // Set sprite colours so player 1 is always // red but either player can go first for (i = 0; i < 56; i++) { if (player == PLAYER1) chip[i].u = ((i%2)<<5); else chip[i].u = ((1-(i%2))<<5); chip[i].r = chip[i].g = chip[i].b = 128; } // Reset board and sprite pointer array for (j = 0; j < 7; j++) { for (i = 0; i < 8; i++) { board[i][j] = 0; wincell[i][j] = 0; pboardchip[i][j] = NULL; } } } // Handles player input void HandlePads() { padStatus = padStatus2 = PadRead(); // Quit if both Select and Start pressed if (padStatus & PADselect && padStatus & PADstart) exit_game = TRUE; if (player == PLAYER2) padStatus2 = (padStatus>>16); // Empty rack if (padStatus & (PADtriangle | (PADtriangle<<16)) && (mode == SHOWWINNER || mode == DROPCHIP)) { int i; lever.r = lever.g = lever.b = 96; lever.y--; fallamount = 1; // Reset brightness before dropping for (i = 0; i < 56; i++) { chip[i].r = chip[i].g = chip[i].b = 128; } if (chiptr > 6) SsUtKeyOn(vab_id, 6, 0, 48, 0, 127, 127); // Release all chips sound (more than 4) else SsUtKeyOn(vab_id, 0, 0, 48, 0, 127, 127); // Dropping sound mode = RELEASECHIPS; } } // Draws board and all active chips void DrawBoard() { int i, j; activeBuff = GsGetActiveBuff(); // Set packet work base GsSetWorkBase((PACKET *)GpuPacketArea[activeBuff]); // Clear the Ordering Table GsClearOt(0, 0, &WorldOT[activeBuff]); #ifdef DEBUG for (j = 0; j < 7; j++) { for (i = 0; i < 8; i++) { FntPrint("%d ", board[i][j]); } FntPrint("\n"); } // Display H-Sync counter FntPrint("\nmode = %d\n", mode); FntPrint("chiptr = %d\n", chiptr); FntPrint("column = %d\n", column); FntPrint("hit_sound = %d\n", hit_sound); FntPrint("HSyncs: %d\n\n", hsyncs); #endif // Sort board and chips into OT for (j = 0; j < 7; j++) { for (i = 0; i < 8; i++) { GsSortFastSprite(&BOARD[i][j], &WorldOT[activeBuff], 0); } } if (mode && mode != INSTRUCTIONS) { for (i = 0; i < (chiptr+1); i++) GsSortFastSprite(&chip[i], &WorldOT[activeBuff], 0); } GsSortFastSprite(&lever, &WorldOT[activeBuff], 0); GsSortFastSprite(&stand[0], &WorldOT[activeBuff], 0); GsSortFastSprite(&stand[1], &WorldOT[activeBuff], 0); DrawGradientBg(); FntFlush(-1); // Wait for end of drawing, a vertical blank then swap buffers DrawSync(0); hsyncs = VSync(0); GsSwapDispBuff(); // Register Clear in OT GsSortClear(0, 0, 0, &WorldOT[activeBuff]); // Draw the Ordering Table GsDrawOt(&WorldOT[activeBuff]); } // Initialises Video, the Frame Buffer and the game sprites void InitGraphics() { int i, j; RECT r; GsIMAGE im; // Setup graphics PAL 320x256 SetVideoMode(MODE_PAL); GsInitGraph(320, 256, 4, 0, 0); GsDISPENV.screen.y = 20; GsDISPENV.screen.h = 256; GsDefDispBuff(0, 0, 0, 256); // Init Ordering Tables for (i = 0; i < 2; i++) { WorldOT[i].length = OT_LENGTH; WorldOT[i].org = OTTag[i]; GsClearOt(0, 0, &WorldOT[i]); } // Loads the font into Frame Buffer FntLoad(960, 256); // Font printing location FntOpen(40, 30, 208, 208, 0, 512); // Put TIM info into im GsGetTimInfo(((u_long *)CHIPS_ADDR)+1, &im); // Load image and clut into frame buffer r.x = im.px; r.y = im.py; r.w = im.pw; r.h = im.ph; LoadImage(&r, im.pixel); r.x = im.cx; r.y = im.cy; r.w = im.cw; r.h = im.ch; LoadImage(&r, im.clut); for (j = 0; j < 7; j++) { for (i = 0; i < 8; i++) { BOARD[i][j].attribute = (im.pmode<<24); BOARD[i][j].x = 24 + 34 * i; BOARD[i][j].y = 16 + 32 * j; BOARD[i][j].w = 34; BOARD[i][j].h = 32; BOARD[i][j].tpage = GetTPage(im.pmode, 0, im.px, im.py); BOARD[i][j].cx = im.cx; BOARD[i][j].cy = im.cy; BOARD[i][j].u = 64; BOARD[i][j].v = 0; BOARD[i][j].r = BOARD[i][j].g = BOARD[i][j].b = 128; } } for (i = 0; i < 56; i++) { chip[i].attribute = (im.pmode<<24); chip[i].x = chip[i].y = 0; chip[i].w = 32; chip[i].h = 32; chip[i].tpage = GetTPage(im.pmode, 0, im.px, im.py); chip[i].cx = im.cx; chip[i].cy = im.cy; chip[i].u = ((i%2)<<5); chip[i].v = 0; chip[i].r = chip[i].g = chip[i].b = 128; } // Put TIM info into im GsGetTimInfo(((u_long *)LEVER_ADDR)+1, &im); // Load image and clut into frame buffer r.x = im.px; r.y = im.py; r.w = im.pw; r.h = im.ph; LoadImage(&r, im.pixel); r.x = im.cx; r.y = im.cy; r.w = im.cw; r.h = im.ch; LoadImage(&r, im.clut); lever.attribute = (im.pmode<<24); lever.x = 24; lever.y = 240; lever.w = 272; lever.h = 10; lever.tpage = GetTPage(im.pmode, 0, im.px, im.py); lever.cx = im.cx; lever.cy = im.cy; lever.u = lever.v = 0; lever.r = lever.g = lever.b = 128; // Put TIM info into im GsGetTimInfo(((u_long *)STAND_ADDR)+1, &im); // Load image and clut into frame buffer r.x = im.px; r.y = im.py; r.w = im.pw; r.h = im.ph; LoadImage(&r, im.pixel); r.x = im.cx; r.y = im.cy; r.w = im.cw; r.h = im.ch; LoadImage(&r, im.clut); for (i=0; i<2; i++) { stand[i].attribute = (im.pmode<<24); stand[i].y = 14; stand[i].w = 10; stand[i].h = 248; stand[i].tpage = GetTPage(im.pmode, 0, im.px, im.py); stand[i].cx = im.cx; stand[i].cy = im.cy; stand[i].u = stand[i].v = 0; stand[i].r = stand[i].g = stand[i].b = 128; } stand[0].x = 14; stand[1].x = 296; } // Initialises sound system void InitSounds() { // Set main volume SsSetMVol(127,127); // Put samples in sound RAM and get vab_id vab_id = SsVabTransfer((u_char *)VH_ADDR, (u_char *)VB_ADDR, 1, 1); } // Coloured gradient background void DrawGradientBg() { int i; for (i=0; i < 256; i++) { gline.b0 = (i>>2); gline.g1 = gline.b0; gline.r1 = 64 - gline.g1; gline.y0 = i; gline.y1 = i; GsSortGLine(&gline, &WorldOT[activeBuff], 1); } } // ****** PAD routines ****** // call once only in program initialisation void PadInit (void) { GetPadBuf(&bb0, &bb1); } // call once per VSync(0) // puts controller pad status into unsigned long integer // please refer to the manuals if you want explanation // of the internals of this function u_long PadRead(void) { return(~(*(bb0+3) | *(bb0+2) << 8 | *(bb1+3) << 16 | *(bb1+2) << 24)); }