// // HYPEROID - a neato game // // Version: 1.1 Copyright (C) 1990,91 Hutchins Software // This software is licenced under the GNU General Public Licence // Please read the associated legal documentation // Author: Edward Hutchins // Internet: eah1@cec1.wustl.edu // USNail: c/o Edward Hutchins, 63 Ridgemoor Dr., Clayton, MO, 63105 // Revisions: // 10/31/91 made game better/harder - Ed. // // Music: R.E.M./The Cure/Ministry/Front 242/The Smiths/New Order/Hendrix... // Beers: Bass Ale, Augsberger Dark // #include "hyperoid.h" // // imports // IMPORT POINT LetterPart[] FROM( roidsupp.c ); IMPORT NPSTR szNumberDesc[] FROM( roidsupp.c ); IMPORT NPSTR szLetterDesc[] FROM( roidsupp.c ); // // globals // GLOBAL CHAR szAppName[32]; GLOBAL HANDLE hAppInst; GLOBAL HWND hAppWnd; GLOBAL HPALETTE hAppPalette; GLOBAL INT nDrawDelay; GLOBAL INT nLevel; GLOBAL INT nSafe; GLOBAL INT nShield; GLOBAL INT nBomb; GLOBAL INT nBadGuys; GLOBAL LONG lScore; GLOBAL LONG lLastLife; GLOBAL LONG lHighScore; GLOBAL BOOL bRestart; GLOBAL BOOL bPaused; GLOBAL BOOL bBW; GLOBAL INT vkShld; GLOBAL INT vkClkw; GLOBAL INT vkCtrClkw; GLOBAL INT vkThrst; GLOBAL INT vkRvThrst; GLOBAL INT vkFire; GLOBAL INT vkBomb; GLOBAL NPOBJ npPlayer; GLOBAL LIST FreeList; GLOBAL LIST RoidList; GLOBAL LIST ShotList; GLOBAL LIST FlameList; GLOBAL LIST SpinnerList; GLOBAL LIST HunterList; GLOBAL LIST HunterShotList; GLOBAL LIST SwarmerList; GLOBAL LIST LetterList; GLOBAL LIST BonusList; GLOBAL INT nCos[DEGREE_SIZE]; GLOBAL INT nSin[DEGREE_SIZE]; GLOBAL HPEN hPen[PALETTE_SIZE]; GLOBAL OBJ Obj[MAX_OBJS]; GLOBAL HBITMAP hBitmap[IDB_MAX]; // // locals // LOCAL DWORD dwSeed; LOCAL INT nScoreLen; LOCAL CHAR szScore[40]; LOCAL RECT rectScoreClip; LOCAL RECT rectShotClip; LOCAL POINT Player[] = { {0, 0}, {160, 150}, {0, 250}, {96, 150}, {0, 0} }; LOCAL POINT Spinner[] = { {160, 150}, {224, 100}, {96, 100}, {32, 150}, {160, 150} }; LOCAL POINT Swarmer[] = { {0, 100}, {64, 100}, {128, 100}, {192, 100}, {0, 100} }; LOCAL POINT Hunter[] = { {160, 150}, {0, 250}, {192, 30}, {64, 30}, {0, 250}, {96, 150}, {128, 150}, {160, 150} }; LOCAL POINT Bonus[] = { {0, 150}, {102, 150}, {205, 150}, {51, 150}, {154, 150}, {0, 150} }; // // KillBadGuy - kill off a badguy (made into a macro) // #define KillBadGuy() \ ((--nBadGuys <= 0)?(SetRestart( RESTART_NEXTLEVEL ),TRUE):FALSE) // // arand - pseudorandom number from 0 to x-1 (thanks antman!) // INT NEAR PASCAL arand( INT x ) { dwSeed = dwSeed * 0x343fd + 0x269ec3; return( (INT)(((dwSeed >> 16) & 0x7fff) * x >> 15) ); } // // AddHead - add an object to the head of a list // VOID NEAR PASCAL AddHead( NPLIST npList, NPNODE npNode ) { if (npList->npHead) { npNode->npNext = npList->npHead; npNode->npPrev = NULL; npList->npHead = (npList->npHead->npPrev = npNode); } else // add to an empty list { npList->npHead = npList->npTail = npNode; npNode->npNext = npNode->npPrev = NULL; } } // // RemHead - remove the first element in a list // NPNODE NEAR PASCAL RemHead( NPLIST npList ) { if (npList->npHead) { NPNODE npNode = npList->npHead; if (npList->npTail != npNode) { npList->npHead = npNode->npNext; npNode->npNext->npPrev = NULL; } else npList->npHead = npList->npTail = NULL; return( npNode ); } else return( NULL ); } // // Remove - remove an arbitrary element from a list // VOID NEAR PASCAL Remove( NPLIST npList, NPNODE npNode ) { if (npNode->npPrev) npNode->npPrev->npNext = npNode->npNext; else npList->npHead = npNode->npNext; if (npNode->npNext) npNode->npNext->npPrev = npNode->npPrev; else npList->npTail = npNode->npPrev; } // // DrawObject - draw a single object // VOID NEAR PASCAL DrawObject( HDC hDC, NPOBJ npObj ) { INT nCnt; INT nDir = (npObj->nDir += npObj->nSpin); INT x = (npObj->Pos.x += npObj->Vel.x); INT y = (npObj->Pos.y += npObj->Vel.y); POINT Pts[MAX_PTS]; if (x < -CLIP_COORD) npObj->Pos.x = x = CLIP_COORD; else if (x > CLIP_COORD) npObj->Pos.x = x = -CLIP_COORD; if (y < -CLIP_COORD) npObj->Pos.y = y = CLIP_COORD; else if (y > CLIP_COORD) npObj->Pos.y = y = -CLIP_COORD; for (nCnt = npObj->byPts - 1; nCnt >= 0; --nCnt) { WORD wDeg = DEG( npObj->Pts[nCnt].x + nDir ); INT nLen = npObj->Pts[nCnt].y; Pts[nCnt].x = x + MULDEG( nLen, nCos[wDeg] ); Pts[nCnt].y = y + MULDEG( nLen, nSin[wDeg] ); } if (npObj->byPts > 1) { SelectObject( hDC, hPen[BLACK] ); Polyline( hDC, npObj->Old, npObj->byPts ); if (npObj->nCount > 0) { SelectObject( hDC, hPen[npObj->byColor] ); Polyline( hDC, Pts, npObj->byPts ); for (nCnt = npObj->byPts - 1; nCnt >= 0; --nCnt) npObj->Old[nCnt] = Pts[nCnt]; } } else // just a point { SetPixel( hDC, npObj->Old[0].x, npObj->Old[0].y, PALETTEINDEX( BLACK ) ); if (npObj->nCount > 0) { SetPixel( hDC, Pts[0].x, Pts[0].y, PALETTEINDEX( npObj->byColor ) ); npObj->Old[0] = Pts[0]; } } } // // SetRestart - set the restart timer // VOID NEAR PASCAL SetRestart( RESTART_MODE Restart ) { POINT Pt; CHAR szBuff[32]; if (bRestart) return; SetTimer( hAppWnd, RESTART_TIMER, RESTART_DELAY, NULL ); bRestart = TRUE; Pt.x = Pt.y = 0; switch (Restart) { case RESTART_GAME: SpinLetters( "GAME OVER", Pt, Pt, RED, 400 ); break; case RESTART_LEVEL: PrintLetters( "GET READY", Pt, Pt, BLUE, 300 ); break; case RESTART_NEXTLEVEL: wsprintf( szBuff, "LEVEL %u", nLevel + 1 ); PrintLetters( szBuff, Pt, Pt, BLUE, 300 ); break; } } // // PrintPlayerMessage - show the player a status message // VOID NEAR PASCAL PrintPlayerMessage( NPSTR npszText ) { POINT Pos, Vel; Pos = npPlayer->Pos; Pos.y -= 400; Vel.x = 0; Vel.y = -50; PrintLetters( npszText, Pos, Vel, GREEN, 150 ); } // // AddExtraLife - give the player another life // VOID NEAR PASCAL AddExtraLife( VOID ) { PrintPlayerMessage( "EXTRA LIFE" ); ++npPlayer->nCount; npPlayer->byColor = (BYTE)(BLACK + npPlayer->nCount); if (npPlayer->byColor > WHITE) npPlayer->byColor = WHITE; } // // Hit - something hit an object, do fireworks // VOID NEAR PASCAL Hit( HDC hDC, NPOBJ npObj ) { INT nCnt; for (nCnt = 0; nCnt < 6; ++nCnt) { NPOBJ npFlame = RemHeadObj( &FreeList ); if (!npFlame) return; npFlame->Pos.x = npObj->Pos.x; npFlame->Pos.y = npObj->Pos.y; npFlame->Vel.x = npObj->Vel.x; npFlame->Vel.y = npObj->Vel.y; npFlame->nDir = npObj->nDir + (nCnt * DEGREE_SIZE) / 6; npFlame->nSpin = 0; npFlame->nCount = 10 + arand( 8 ); npFlame->byColor = YELLOW; npFlame->byPts = 1; npFlame->Pts[0].x = npFlame->Pts[0].y = 0; ACCEL( npFlame, npFlame->nDir, 50 - npFlame->nCount ); AddHeadObj( &FlameList, npFlame ); } } // // Explode - explode an object // VOID NEAR PASCAL Explode( HDC hDC, NPOBJ npObj ) { INT nCnt, nSize = npObj->byPts; DrawObject( hDC, npObj ); for (nCnt = 0; nCnt < nSize; ++nCnt) { NPOBJ npFlame; if (arand( 2 )) continue; if (!(npFlame = RemHeadObj( &FreeList ))) return; npFlame->Pos.x = npObj->Pos.x; npFlame->Pos.y = npObj->Pos.y; npFlame->Vel.x = npObj->Vel.x; npFlame->Vel.y = npObj->Vel.y; npFlame->nDir = npObj->nDir + nCnt * DEGREE_SIZE / nSize + arand( 32 ); npFlame->nSpin = arand( 31 ) - 15; npFlame->nCount = 25 + arand( 16 ); npFlame->byColor = npObj->byColor; npFlame->byPts = 2; npFlame->Pts[0] = npObj->Pts[nCnt]; if (nCnt == nSize - 1) npFlame->Pts[1] = npObj->Pts[0]; else npFlame->Pts[1] = npObj->Pts[nCnt + 1]; ACCEL( npFlame, npFlame->nDir, 60 - npFlame->nCount ); AddHeadObj( &FlameList, npFlame ); } Hit( hDC, npObj ); } // // HitPlayer - blow up the player // BOOL NEAR PASCAL HitPlayer( HDC hDC, NPOBJ npObj ) { POINT Vel; INT nMass, nSpin; if (nSafe || (npPlayer->nCount <= 0)) return( FALSE ); // rumble and shake both objects nMass = npPlayer->nMass + npObj->nMass; nSpin = npPlayer->nSpin + npObj->nSpin; npObj->nSpin -= MulDiv( nSpin, npPlayer->nMass, nMass ); npPlayer->nSpin -= MulDiv( nSpin, npObj->nMass, nMass ); Vel.x = npPlayer->Vel.x - npObj->Vel.x; Vel.y = npPlayer->Vel.y - npObj->Vel.y; npObj->Vel.x += MulDiv( Vel.x, npPlayer->nMass, nMass ); npObj->Vel.y += MulDiv( Vel.y, npPlayer->nMass, nMass ); npPlayer->Vel.x -= MulDiv( Vel.x, npObj->nMass, nMass ); npPlayer->Vel.y -= MulDiv( Vel.y, npObj->nMass, nMass ); if (--npPlayer->nCount) { npPlayer->byColor = (BYTE)(BLACK + npPlayer->nCount); if (npPlayer->byColor > WHITE) npPlayer->byColor = WHITE; Hit( hDC, npPlayer ); return( TRUE ); } // final death npPlayer->byColor = WHITE; Explode( hDC, npPlayer ); SetRestart( RESTART_GAME ); return( FALSE ); } // // CreateLetter - make a new letter object // NPOBJ FAR PASCAL CreateLetter( CHAR cLetter, INT nSize ) { NPOBJ npLtr; INT nCnt; NPSTR npDesc; if (cLetter >= '0' && cLetter <= '9') npDesc = szNumberDesc[cLetter - '0']; else if (cLetter >= 'A' && cLetter <= 'Z') npDesc = szLetterDesc[cLetter - 'A']; else if (cLetter >= 'a' && cLetter <= 'z') npDesc = szLetterDesc[cLetter - 'a']; else if (cLetter == '.') npDesc = "l"; else return( NULL ); if (npLtr = RemHeadObj( &FreeList )) { npLtr->nMass = 1; npLtr->nDir = 0; npLtr->nSpin = 0; npLtr->nCount = 40; npLtr->byColor = WHITE; npLtr->byPts = (BYTE)(nCnt = strlen( npDesc )); while (nCnt--) { npLtr->Pts[nCnt] = LetterPart[npDesc[nCnt] - 'a']; npLtr->Pts[nCnt].y = MulDiv( npLtr->Pts[nCnt].y, nSize, LETTER_MAX ); } AddHeadObj( &LetterList, npLtr ); } return( npLtr ); } // // DrawLetters - draw letters and such // VOID NEAR PASCAL DrawLetters( HDC hDC ) { NPOBJ npLtr, npNext; for (npLtr = HeadObj( &LetterList ); npLtr; npLtr = npNext) { npNext = NextObj( npLtr ); switch (--npLtr->nCount) { case 3: --npLtr->byColor; break; case 0: RemoveObj( &LetterList, npLtr ); AddHeadObj( &FreeList, npLtr ); break; } DrawObject( hDC, npLtr ); } } // // CreateBonus - make a new bonus object // VOID NEAR PASCAL CreateBonus( VOID ) { NPOBJ npBonus; INT nCnt; if (npBonus = RemHeadObj( &FreeList )) { npBonus->Pos.x = arand( CLIP_COORD * 2 ) - CLIP_COORD; npBonus->Pos.y = -CLIP_COORD; npBonus->Vel.x = npBonus->Vel.y = 0; npBonus->nDir = arand( DEGREE_SIZE ); npBonus->nSpin = (arand( 2 ) ? 12 : -12); npBonus->nCount = arand( 4 ) + 1; npBonus->nDelay = 64 + arand( 128 ); npBonus->nMass = 1; npBonus->byColor = (BYTE)(WHITE + (npBonus->nCount * 2)); npBonus->byPts = DIM(Bonus); for (nCnt = 0; nCnt < DIM(Bonus); ++nCnt) npBonus->Pts[nCnt] = Bonus[nCnt]; ACCEL( npBonus, npBonus->nDir, 30 + nLevel * 2 ); AddHeadObj( &BonusList, npBonus ); } } // // DrawBonuses - process and draw the bonus list // VOID NEAR PASCAL DrawBonuses( HDC hDC ) { NPOBJ npBonus, npNext; LOCAL INT nNextBonus = 1000; if (nBadGuys && (--nNextBonus < 0)) { CreateBonus(); nNextBonus = 1000; } for (npBonus = HeadObj( &BonusList ); npBonus; npBonus = npNext) { NPOBJ npShot; INT nDelta; RECT rect; npNext = NextObj( npBonus ); MKRECT( &rect, npBonus->Pos, 150 ); if (PTINRECT( &rect, npPlayer->Pos )) { if (npPlayer->nCount > 0) switch (npBonus->nCount) { case 1: { CHAR szBuff[32]; LONG lBonus = 1000L * nLevel; if (lBonus == 0) lBonus = 500; lScore += lBonus; wsprintf( szBuff, "%ld", lBonus ); PrintPlayerMessage( szBuff ); } break; case 2: nSafe = 15; ++nShield; npPlayer->byColor = GREEN; PrintPlayerMessage( "EXTRA SHIELD" ); break; case 3: ++nBomb; PrintPlayerMessage( "EXTRA BOMB" ); break; case 4: AddExtraLife(); break; } npBonus->nCount = 0; Explode( hDC, npBonus ); RemoveObj( &BonusList, npBonus ); AddHeadObj( &FreeList, npBonus ); } else if (INTRECT(&rect, &rectShotClip)) { for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) { if (!PTINRECT( &rect, npShot->Pos )) continue; npShot->nCount = 1; npBonus->nCount = 0; Explode( hDC, npBonus ); RemoveObj( &BonusList, npBonus ); AddHeadObj( &FreeList, npBonus ); } } if (npBonus->nCount && --npBonus->nDelay <= 0) { --npBonus->nCount; npBonus->nDelay = 64 + arand( 128 ); npBonus->byColor = (BYTE)(WHITE + (npBonus->nCount * 2)); if (npBonus->nCount == 0) { Explode( hDC, npBonus ); RemoveObj( &BonusList, npBonus ); AddHeadObj( &FreeList, npBonus ); } } nDelta = npPlayer->Pos.x - npBonus->Pos.x; while (nDelta < -16 || nDelta > 16) nDelta /= 2; npBonus->Vel.x += nDelta - npBonus->Vel.x / 16; nDelta = npPlayer->Pos.y - npBonus->Pos.y; while (nDelta < -16 || nDelta > 16) nDelta /= 2; npBonus->Vel.y += nDelta - npBonus->Vel.y / 16; DrawObject( hDC, npBonus ); } } // // DrawHunterShots - process and draw the hunter shot list // VOID NEAR PASCAL DrawHunterShots( HDC hDC ) { NPOBJ npShot, npNext; for (npShot = HeadObj( &HunterShotList ); npShot; npShot = npNext) { RECT rect; npNext = NextObj( npShot ); MKRECT( &rect, npShot->Pos, 200 ); if (PTINRECT( &rect, npPlayer->Pos )) { HitPlayer( hDC, npShot ); npShot->nCount = 1; } switch (--npShot->nCount) { case 7: npShot->byColor = DKGREEN; break; case 0: RemoveObj( &HunterShotList, npShot ); AddHeadObj( &FreeList, npShot ); break; } DrawObject( hDC, npShot ); } } // // FireHunterShot - fire a hunter bullet // VOID NEAR PASCAL FireHunterShot( NPOBJ npHunt ) { NPOBJ npShot; if (npShot = RemHeadObj( &FreeList )) { npShot->Pos.x = npHunt->Pos.x; npShot->Pos.y = npHunt->Pos.y; npShot->Vel.x = npHunt->Vel.x; npShot->Vel.y = npHunt->Vel.y; npShot->nMass = 8; npShot->nDir = npHunt->nDir + arand( 5 ) - 2; npShot->nSpin = (arand( 2 ) ? 10 : -10); npShot->nCount = 16 + arand( 8 ); npShot->byColor = GREEN; npShot->byPts = 2; npShot->Pts[0].x = 128; npShot->Pts[0].y = 50; npShot->Pts[1].x = 0; npShot->Pts[1].y = 50; ACCEL( npShot, npShot->nDir, 200 + npShot->nCount ); AddHeadObj( &HunterShotList, npShot ); } } // // CreateHunter - make a new hunter // VOID NEAR PASCAL CreateHunter( VOID ) { NPOBJ npHunt; INT nCnt; if (npHunt = RemHeadObj( &FreeList )) { npHunt->Pos.x = arand( CLIP_COORD * 2 ) - CLIP_COORD; npHunt->Pos.y = -CLIP_COORD; npHunt->Vel.x = npHunt->Vel.y = 0; npHunt->nMass = 256; npHunt->nDir = arand( DEGREE_SIZE ); npHunt->nSpin = 0; npHunt->nCount = 1 + arand( nLevel ); npHunt->nDelay = 2 + arand( 10 ); npHunt->byColor = CYAN; npHunt->byPts = DIM(Hunter); for (nCnt = 0; nCnt < DIM(Hunter); ++nCnt) npHunt->Pts[nCnt] = Hunter[nCnt]; ACCEL( npHunt, npHunt->nDir, 30 + nLevel * 2 ); AddHeadObj( &HunterList, npHunt ); ++nBadGuys; } } // // DrawHunters - process and draw the hunter list // VOID NEAR PASCAL DrawHunters( HDC hDC ) { NPOBJ npHunt, npNext; LOCAL INT nNextHunter = 200; if (nBadGuys && (--nNextHunter < 0)) { CreateHunter(); nNextHunter = 1000 + arand( 1000 ) - nLevel * 8; } for (npHunt = HeadObj( &HunterList ); npHunt; npHunt = npNext) { NPOBJ npShot; RECT rect; npNext = NextObj( npHunt ); MKRECT( &rect, npHunt->Pos, 200 ); if (PTINRECT( &rect, npPlayer->Pos )) { HitPlayer( hDC, npHunt ); --npHunt->nCount; if (npHunt->nCount < 1) { KillBadGuy(); npHunt->byColor = CYAN; Explode( hDC, npHunt ); RemoveObj( &HunterList, npHunt ); AddHeadObj( &FreeList, npHunt ); } else if (npHunt->nCount == 1) npHunt->byColor = DKCYAN; } else if (INTRECT(&rect, &rectShotClip)) { for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) { if (!PTINRECT( &rect, npShot->Pos )) continue; npShot->nCount = 1; lScore += npHunt->nCount * 1000; if (--npHunt->nCount < 1) { KillBadGuy(); npHunt->byColor = CYAN; Explode( hDC, npHunt ); RemoveObj( &HunterList, npHunt ); AddHeadObj( &FreeList, npHunt ); } else { if (npHunt->nCount == 1) npHunt->byColor = DKCYAN; Hit( hDC, npHunt ); } break; } } ACCEL( npHunt, npHunt->nDir, 8 ); npHunt->Vel.x -= npHunt->Vel.x / 16; npHunt->Vel.y -= npHunt->Vel.y / 16; if (--npHunt->nDelay <= 0) { npHunt->nDelay = arand( 10 ); npHunt->nSpin = arand( 11 ) - 5; FireHunterShot( npHunt ); } DrawObject( hDC, npHunt ); } } // // CreateSwarmer - make a new swarmer // VOID NEAR PASCAL CreateSwarmer( POINT Pos, INT nDir, INT nCount ) { NPOBJ npSwarm; INT nCnt; if (npSwarm = RemHeadObj( &FreeList )) { npSwarm->Pos = Pos; npSwarm->Vel.x = npSwarm->Vel.y = 0; npSwarm->nDir = nDir; npSwarm->nSpin = arand( 31 ) - 15; npSwarm->nCount = nCount; npSwarm->nDelay = 64 + arand( 64 ); npSwarm->nMass = 32; npSwarm->byColor = DKGREEN; npSwarm->byPts = DIM(Swarmer); for (nCnt = 0; nCnt < DIM(Swarmer); ++nCnt) { npSwarm->Pts[nCnt] = Swarmer[nCnt]; npSwarm->Pts[nCnt].y += nCount * 10; } ACCEL( npSwarm, npSwarm->nDir, 30 + nLevel * 2 ); AddHeadObj( &SwarmerList, npSwarm ); ++nBadGuys; } } // // DrawSwarmers - process and draw the swarmer list // VOID NEAR PASCAL DrawSwarmers( HDC hDC ) { NPOBJ npSwarm, npNext; LOCAL INT nNextSwarmer = 1000; if (nBadGuys && (--nNextSwarmer < 0)) { POINT Pos; Pos.x = arand( CLIP_COORD * 2 ) - CLIP_COORD; Pos.y = -CLIP_COORD; CreateSwarmer( Pos, arand( DEGREE_SIZE ), 8 + nLevel * 2 ); nNextSwarmer = 1000 + arand( 500 ) - nLevel * 4; } for (npSwarm = HeadObj( &SwarmerList ); npSwarm; npSwarm = npNext) { NPOBJ npShot; RECT rect; npNext = NextObj( npSwarm ); MKRECT( &rect, npSwarm->Pos, 150 + npSwarm->nCount * 10 ); if (PTINRECT( &rect, npPlayer->Pos )) { HitPlayer( hDC, npSwarm ); npSwarm->nCount = 0; } else if (INTRECT(&rect, &rectShotClip)) { for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) { if (!PTINRECT( &rect, npShot->Pos )) continue; npShot->nCount = 1; lScore += npSwarm->nCount * 25; npSwarm->nCount = 0; break; } } if (npSwarm->nCount <= 0) { npSwarm->byColor = GREEN; KillBadGuy(); Explode( hDC, npSwarm ); RemoveObj( &SwarmerList, npSwarm ); AddHeadObj( &FreeList, npSwarm ); } else { if ((npSwarm->nCount > 1) && (--npSwarm->nDelay <= 0)) { INT nDir = arand( DEGREE_SIZE ); INT nCount = npSwarm->nCount / 2; CreateSwarmer( npSwarm->Pos, nDir, nCount ); nCount = npSwarm->nCount - nCount; CreateSwarmer( npSwarm->Pos, nDir + 128, nCount ); npSwarm->nCount = 0; } DrawObject( hDC, npSwarm ); } } } // // CreateSpinner - make a new spinner // VOID NEAR PASCAL CreateSpinner( VOID ) { NPOBJ npSpin; INT nCnt; if (npSpin = RemHeadObj( &FreeList )) { npSpin->Pos.x = arand( CLIP_COORD * 2 ) - CLIP_COORD; npSpin->Pos.y = -CLIP_COORD; npSpin->Vel.x = npSpin->Vel.y = 0; npSpin->nDir = arand( DEGREE_SIZE ); npSpin->nSpin = -12; npSpin->nCount = 1 + arand( nLevel ); npSpin->nMass = 64 + npSpin->nCount * 32; npSpin->byColor = (BYTE)(MAGENTA - npSpin->nCount); npSpin->byPts = DIM(Spinner); for (nCnt = 0; nCnt < DIM(Spinner); ++nCnt) npSpin->Pts[nCnt] = Spinner[nCnt]; ACCEL( npSpin, npSpin->nDir, 30 + nLevel * 2 ); AddHeadObj( &SpinnerList, npSpin ); ++nBadGuys; } } // // DrawSpinners - process and draw the spinner list // VOID NEAR PASCAL DrawSpinners( HDC hDC ) { NPOBJ npSpin, npNext; LOCAL INT nNextSpinner = 1000; if (nBadGuys && (--nNextSpinner < 0)) { CreateSpinner(); nNextSpinner = 100 + arand( 900 ) - nLevel * 2; } for (npSpin = HeadObj( &SpinnerList ); npSpin; npSpin = npNext) { NPOBJ npShot; INT nDelta; RECT rect; npNext = NextObj( npSpin ); MKRECT( &rect, npSpin->Pos, 150 ); if (PTINRECT( &rect, npPlayer->Pos )) { HitPlayer( hDC, npSpin ); --npSpin->nCount; npSpin->byColor = (BYTE)(MAGENTA - npSpin->nCount); if (npSpin->nCount < 1) { KillBadGuy(); Explode( hDC, npSpin ); RemoveObj( &SpinnerList, npSpin ); AddHeadObj( &FreeList, npSpin ); } } else if (INTRECT(&rect, &rectShotClip)) { for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) { if (!PTINRECT( &rect, npShot->Pos )) continue; npShot->nCount = 1; lScore += npSpin->nCount * 500; npSpin->byColor = (BYTE)(MAGENTA - (--npSpin->nCount)); if (npSpin->nCount < 1) { KillBadGuy(); Explode( hDC, npSpin ); RemoveObj( &SpinnerList, npSpin ); AddHeadObj( &FreeList, npSpin ); } else Hit( hDC, npSpin ); break; } } nDelta = npPlayer->Pos.x - npSpin->Pos.x; while (nDelta < -16 || nDelta > 16) nDelta /= 2; npSpin->Vel.x += nDelta - npSpin->Vel.x / 16; nDelta = npPlayer->Pos.y - npSpin->Pos.y; while (nDelta < -16 || nDelta > 16) nDelta /= 2; npSpin->Vel.y += nDelta - npSpin->Vel.y / 16; DrawObject( hDC, npSpin ); } } // // CreateRoid - make a new asteroid // VOID NEAR PASCAL CreateRoid( POINT Pos, POINT Vel, INT nSides, BYTE byColor, INT nDir, INT nSpeed, INT nSpin ) { NPOBJ npRoid; INT nCnt; if (npRoid = RemHeadObj( &FreeList )) { npRoid->Pos = Pos; npRoid->Vel = Vel; npRoid->nMass = nSides * 128; npRoid->nDir = nDir; npRoid->nSpin = nSpin + arand( 11 ) - 5; npRoid->nCount = nSides * 100; npRoid->byColor = byColor; npRoid->byPts = (BYTE)(nSides + 1); for (nCnt = 0; nCnt < nSides; ++nCnt) { npRoid->Pts[nCnt].x = nCnt * DEGREE_SIZE / nSides + arand( 30 ); npRoid->Pts[nCnt].y = (nSides - 1) * 100 + 20 + arand( 80 ); } npRoid->Pts[nSides] = npRoid->Pts[0]; ACCEL( npRoid, nDir, nSpeed ); AddHeadObj( &RoidList, npRoid ); ++nBadGuys; } } // // BreakRoid - break up an asteroid // VOID NEAR PASCAL BreakRoid( HDC hDC, NPOBJ npRoid, NPOBJ npShot ) { INT nCnt, nNew; lScore += npRoid->nCount; if (npShot) npShot->nCount = 1; switch (npRoid->byPts) { case 8: nNew = 2 + arand( 3 ); break; case 7: nNew = 1 + arand( 3 ); break; case 6: nNew = 1 + arand( 2 ); break; case 5: nNew = arand( 2 ); break; default: nNew = 0; break; } if (nNew == 1) // don't explode outward { POINT Pt = npRoid->Pos; Pt.x += arand( 301 ) - 150; Pt.y += arand( 301 ) - 150; CreateRoid( Pt, npRoid->Vel, npRoid->byPts - (nNew + 1), npRoid->byColor, npShot->nDir, 8, npRoid->nSpin ); } else if (nNew > 0) { INT nSpeed = npRoid->nSpin * npRoid->nSpin * nNew + 16; for (nCnt = 0; nCnt < nNew; ++nCnt) { POINT Pt = npRoid->Pos; Pt.x += arand( 601 ) - 300; Pt.y += arand( 601 ) - 300; CreateRoid( Pt, npRoid->Vel, npRoid->byPts - (nNew + 1), npRoid->byColor, npRoid->nDir + nCnt * DEGREE_SIZE / nNew + arand( 32 ), nSpeed + arand( nLevel * 4 ), npRoid->nSpin / 2 ); } } KillBadGuy(); ++npRoid->byColor; npRoid->nCount = 0; if (nNew) { Hit( hDC, npRoid ); DrawObject( hDC, npRoid ); } else Explode( hDC, npRoid ); RemoveObj( &RoidList, npRoid ); AddHeadObj( &FreeList, npRoid ); } // // DrawRoids - process and draw the asteroid list // VOID NEAR PASCAL DrawRoids( HDC hDC ) { NPOBJ npRoid, npNext; for (npRoid = HeadObj( &RoidList ); npRoid; npRoid = npNext) { INT nSize = npRoid->nCount; NPOBJ npShot; RECT rect; npNext = NextObj( npRoid ); DrawObject( hDC, npRoid ); MKRECT( &rect, npRoid->Pos, nSize ); if (PTINRECT( &rect, npPlayer->Pos ) && HitPlayer( hDC, npRoid )) { npPlayer->nCount = -npPlayer->nCount; npPlayer->byColor = WHITE; Explode( hDC, npPlayer ); BreakRoid( hDC, npRoid, NULL ); if (nBadGuys) SetRestart( RESTART_LEVEL ); else SetRestart( RESTART_NEXTLEVEL ); } else if (INTRECT(&rect, &rectShotClip)) { for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) { if (!PTINRECT( &rect, npShot->Pos )) continue; BreakRoid( hDC, npRoid, npShot ); break; } } } } // // DrawShots - process and draw the player shot list // VOID NEAR PASCAL DrawShots( HDC hDC ) { NPOBJ npShot, npNext; if (npShot = HeadObj( &ShotList )) { rectShotClip.left = rectShotClip.right = npShot->Pos.x; rectShotClip.top = rectShotClip.bottom = npShot->Pos.y; while (npShot) { npNext = NextObj( npShot ); switch (--npShot->nCount) { case 10: npShot->byColor = DKCYAN; break; case 5: npShot->byColor = DKBLUE; break; case 0: RemoveObj( &ShotList, npShot ); AddHeadObj( &FreeList, npShot ); break; } DrawObject( hDC, npShot ); if (npShot->Pos.x < rectShotClip.left) rectShotClip.left = npShot->Pos.x; else if (npShot->Pos.x > rectShotClip.right) rectShotClip.right = npShot->Pos.x; if (npShot->Pos.y < rectShotClip.top) rectShotClip.top = npShot->Pos.y; else if (npShot->Pos.y > rectShotClip.bottom) rectShotClip.bottom = npShot->Pos.y; npShot = npNext; } } else rectShotClip.left = rectShotClip.right = rectShotClip.top = rectShotClip.bottom = 32767; } // // DrawFlames - process and draw the flame list // VOID NEAR PASCAL DrawFlames( HDC hDC ) { NPOBJ npFlame, npNext; for (npFlame = HeadObj( &FlameList ); npFlame; npFlame = npNext) { npNext = NextObj( npFlame ); switch (--npFlame->nCount) { case 7: npFlame->byColor = RED; break; case 3: npFlame->byColor = DKRED; break; case 0: RemoveObj( &FlameList, npFlame ); AddHeadObj( &FreeList, npFlame ); break; } DrawObject( hDC, npFlame ); } } // // FireShot - fire a bullet // VOID NEAR PASCAL FireShot( VOID ) { NPOBJ npShot; if (npShot = RemHeadObj( &FreeList )) { npShot->Pos.x = npPlayer->Pos.x; npShot->Pos.y = npPlayer->Pos.y; npShot->Vel.x = npPlayer->Vel.x; npShot->Vel.y = npPlayer->Vel.y; npShot->nMass = 8; npShot->nDir = npPlayer->nDir + arand( 5 ) - 2; npShot->nSpin = 0; npShot->nCount = 16 + arand( 8 ); npShot->byColor = CYAN; npShot->byPts = 2; npShot->Pts[0].x = 128; npShot->Pts[0].y = 50; npShot->Pts[1].x = 0; npShot->Pts[1].y = 50; ACCEL( npShot, npShot->nDir, 200 + npShot->nCount ); AddHeadObj( &ShotList, npShot ); } } // // AccelPlayer - move the player forward // VOID NEAR PASCAL AccelPlayer( INT nDir, INT nAccel ) { NPOBJ npFlame; nDir += npPlayer->nDir; if (nAccel) ACCEL( npPlayer, nDir, nAccel ); if (npFlame = RemHeadObj( &FreeList )) { npFlame->Pos.x = npPlayer->Pos.x; npFlame->Pos.y = npPlayer->Pos.y; npFlame->Vel.x = npPlayer->Vel.x; npFlame->Vel.y = npPlayer->Vel.y; npFlame->nDir = nDir + 100 + arand( 57 ); npFlame->nSpin = 0; npFlame->nCount = nAccel + arand( 7 ); npFlame->byColor = YELLOW; npFlame->byPts = 1; npFlame->Pts[0].x = npFlame->Pts[0].y = 0; ACCEL( npFlame, npFlame->nDir, 50 + arand( 10 ) ); AddHeadObj( &FlameList, npFlame ); } } // // DrawPlayer - process and draw the player // VOID NEAR PASCAL DrawPlayer( HDC hDC ) { LOCAL INT nBombing = 0; LOCAL INT nShotDelay = 0; if (npPlayer->nCount <= 0) return; if (nSafe > 0) { if (--nSafe == 0) { npPlayer->byColor = (BYTE)(BLACK + npPlayer->nCount); if (npPlayer->byColor > WHITE) npPlayer->byColor = WHITE; } } else if (IsKeyDown( vkShld ) && nShield > 0) { nSafe = 15; if (--nShield > 0) npPlayer->byColor = GREEN; else npPlayer->byColor = DKGREEN; } if (nBombing > 0) { if (--nBombing == 0) { ExplodeBadguys( hDC, &SpinnerList ); ExplodeBadguys( hDC, &SwarmerList ); ExplodeBadguys( hDC, &HunterList ); } else { HitList( hDC, &SpinnerList ); HitList( hDC, &SwarmerList ); HitList( hDC, &HunterList ); } } else if (nBomb && IsKeyDown( vkBomb )) --nBomb, nBombing = 5; if (IsKeyDown( vkClkw )) npPlayer->nSpin += 8; if (IsKeyDown( vkCtrClkw )) npPlayer->nSpin -= 8; if (IsKeyDown( vkThrst )) AccelPlayer( 0, 12 ); if (IsKeyDown( vkRvThrst )) AccelPlayer( 128, 12 ); if (nShotDelay) --nShotDelay; else if (IsKeyDown( vkFire )) FireShot(), nShotDelay = 2; DrawObject( hDC, npPlayer ); npPlayer->nSpin /= 2; } // // GetHyperoidDC - get the correct DC for hyperoid rendering // HDC NEAR PASCAL GetHyperoidDC( HWND hWnd ) { HDC hDC; INT cx, cy; RECT rect; GetClientRect( hWnd, &rect ); cx = rect.right - rect.left; cy = rect.bottom - rect.top; hDC = GetDC( hWnd ); // set up the mapping mode SetMapMode( hDC, MM_ISOTROPIC ); SetWindowExt( hDC, MAX_COORD, MAX_COORD ); SetViewportExt( hDC, cx / 2, -cy / 2 ); SetViewportOrg( hDC, cx / 2, cy / 2 ); // realize the palette SelectPalette( hDC, hAppPalette, 0 ); RealizePalette( hDC ); return( hDC ); } // // DrawObjects - transform and redraw everything in the system // VOID NEAR PASCAL DrawObjects( HWND hWnd ) { HDC hDC = GetHyperoidDC( hWnd ); // move and draw things (I don't think the order is important...) DrawPlayer( hDC ); DrawFlames( hDC ); DrawShots( hDC ); DrawRoids( hDC ); DrawSpinners( hDC ); DrawSwarmers( hDC ); DrawHunters( hDC ); DrawHunterShots( hDC ); DrawLetters( hDC ); DrawBonuses( hDC ); // (...but I'm not changing it!!! :-) ReleaseDC( hWnd, hDC ); } // // SetIndicator - set a quantity indicator // INT NEAR PASCAL SetIndicator( NPSTR npBuff, CHAR IDBitmap, INT nQuant ) { if (nQuant > 5) { *npBuff++ = IDBitmap; *npBuff++ = IDBitmap; *npBuff++ = IDBitmap; *npBuff++ = IDBitmap; *npBuff++ = IDB_plus; } else { INT nBlank = 5 - nQuant; while (nQuant--) *npBuff++ = IDBitmap; while (nBlank--) *npBuff++ = IDB_blank; } return( 5 ); } // // CheckScore - show the score and such stuff // VOID NEAR PASCAL CheckScore( HWND hWnd ) { CHAR szBuff[sizeof(szScore)]; NPSTR npBuff = szBuff; INT nLives, nLen, nCnt, x, y; HBITMAP hbmOld; HDC hDC, hDCMem; if (IsIconic( hWnd )) return; if (lScore - lLastLife > EXTRA_LIFE) { AddExtraLife(); lLastLife = lScore; } nLives = ((npPlayer->nCount > 0) ? npPlayer->nCount : -npPlayer->nCount); *npBuff++ = IDB_level; wsprintf( npBuff, "%2.2u", nLevel ); while (isdigit( *npBuff )) *npBuff = (CHAR)(*npBuff + IDB_num0 - '0'), ++npBuff; *npBuff++ = IDB_blank; *npBuff++ = IDB_score; wsprintf( npBuff, "%7.7lu", lScore ); while (isdigit( *npBuff )) *npBuff = (CHAR)(*npBuff + IDB_num0 - '0'), ++npBuff; *npBuff++ = IDB_blank; npBuff += SetIndicator( npBuff, IDB_life, nLives ); npBuff += SetIndicator( npBuff, IDB_shield, nShield ); npBuff += SetIndicator( npBuff, IDB_bomb, nBomb ); nLen = npBuff - szBuff; hDC = GetWindowDC( hWnd ); IntersectClipRect( hDC, rectScoreClip.left, rectScoreClip.top, rectScoreClip.right, rectScoreClip.bottom ); hDCMem = CreateCompatibleDC( hDC ); hbmOld = SelectObject( hDCMem, hBitmap[0] ); x = rectScoreClip.left; y = rectScoreClip.top; for (nCnt = 0; nCnt < nLen; ++nCnt) { if (szBuff[nCnt] != szScore[nCnt]) { SelectObject( hDCMem, hBitmap[szBuff[nCnt] - IDB_blank] ); BitBlt( hDC, x, y, CX_BITMAP, CY_BITMAP, hDCMem, 0, 0, SRCCOPY ); szScore[nCnt] = szBuff[nCnt]; } x += CX_BITMAP; } if (nCnt < nScoreLen) { SelectObject( hDCMem, hBitmap[0] ); do { if (szScore[nCnt] != IDB_blank) { BitBlt( hDC, x, y, CX_BITMAP, CY_BITMAP, hDCMem, 0, 0, SRCCOPY ); szScore[nCnt] = IDB_blank; } x += CX_BITMAP; } while (++nCnt < nScoreLen); } nScoreLen = nLen; SelectObject( hDCMem, hbmOld ); DeleteDC( hDCMem ); ReleaseDC( hWnd, hDC ); } // // HitList - Hit() a list of things // VOID NEAR PASCAL HitList( HDC hDC, NPLIST npList ) { NPOBJ npObj; for (npObj = HeadObj( npList ); npObj; npObj = NextObj( npObj )) if (npObj->nCount) Hit( hDC, npObj ); } // // ExplodeBadguys - explode a list of badguys // VOID NEAR PASCAL ExplodeBadguys( HDC hDC, NPLIST npList ) { NPOBJ npObj; while (npObj = HeadObj( npList )) { KillBadGuy(); npObj->nCount = 0; Explode( hDC, npObj ); RemoveObj( npList, npObj ); AddHeadObj( &FreeList, npObj ); } } // // NewGame - start a new game // VOID NEAR PASCAL NewGame( HWND hWnd ) { HDC hDC = GetHyperoidDC( hWnd ); npPlayer->nCount = 0; npPlayer->byColor = WHITE; Explode( hDC, npPlayer ); SetRestart( RESTART_GAME ); ExplodeBadguys( hDC, &RoidList ); ExplodeBadguys( hDC, &SpinnerList ); ExplodeBadguys( hDC, &SwarmerList ); ExplodeBadguys( hDC, &HunterList ); ReleaseDC( hWnd, hDC ); } // // RestartHyperoid - set up a game! // VOID NEAR PASCAL RestartHyperoid( VOID ) { if (npPlayer->nCount == 0) { POINT Pos, Vel; Pos.x = 0; Pos.y = -CLIP_COORD / 2; Vel.x = 0; Vel.y = 150; PrintLetters( szAppName, Pos, Vel, YELLOW, 800 ); npPlayer->nCount = 3; if (lHighScore < lScore) lHighScore = lScore; lLastLife = lScore = 0; nLevel = 0; nShield = nBomb = 3; } else if (npPlayer->nCount < 0) { // cheesy way of restarting after a major collision npPlayer->nCount = -npPlayer->nCount; nShield = nBomb = 3; } npPlayer->Pos.x = npPlayer->Pos.y = 0; npPlayer->Vel.x = npPlayer->Vel.y = 0; npPlayer->nDir = 64; npPlayer->nSpin = 0; npPlayer->byColor = GREEN; nSafe = 30; if (ShotList.npHead) { NPOBJ npShot; for (npShot = HeadObj( &ShotList ); npShot; npShot = NextObj( npShot )) npShot->nCount = 1; } // reseed the asteroid field if (nBadGuys == 0) { INT nCnt; ++nLevel; for (nCnt = 5 + nLevel; nCnt; --nCnt) { POINT Pos, Vel; Pos.x = arand( MAX_COORD * 2 ) - MAX_COORD; Pos.y = arand( MAX_COORD * 2 ) - MAX_COORD; Vel.x = Vel.y = 0; CreateRoid( Pos, Vel, 6 + arand( 2 ), (BYTE)(arand( 2 ) ? DKYELLOW : DKGREY), arand( DEGREE_MAX ), 30 + arand( nLevel * 8 ), 0 ); } } } // // Panic - boss key (or just pause) // VOID NEAR PASCAL Panic( BOOL bPanic ) { if (bPanic && !bPaused) { bPaused = TRUE; KillTimer( hAppWnd, DRAW_TIMER ); SetWindowText( hAppWnd, "Program Manager Help - PROGMAN.HLP" ); ShowWindow( hAppWnd, SW_SHOWMINNOACTIVE ); InvalidateRect( hAppWnd, NULL, TRUE ); } else if (bPaused) // double-panic == normal { bPaused = FALSE; SetWindowText( hAppWnd, szAppName ); if (bPanic) ShowWindow( hAppWnd, SW_RESTORE ); SetTimer( hAppWnd, DRAW_TIMER, nDrawDelay, NULL ); } } // // PaintHyperoid - paint the hyperoid window // VOID NEAR PASCAL PaintHyperoid( HWND hWnd ) { PAINTSTRUCT ps; BeginPaint( hWnd, &ps ); if (bPaused) DrawIcon( ps.hdc, 2, 2, LoadIcon( hAppInst, INTRES(IDI_PANIC) ) ); EndPaint( hWnd, &ps ); } // // EraseHyperoidBkgnd - fill in the background // BOOL NEAR PASCAL EraseHyperoidBkgnd( HWND hWnd, HDC hDC ) { HBRUSH hbr; RECT rect; GetClientRect( hWnd, &rect ); if (bPaused) { SetBrushOrg( hDC, 0, 0 ); hbr = CreateSolidBrush( GetSysColor( COLOR_BACKGROUND ) ); } else { SelectPalette( hDC, hAppPalette, 0 ); RealizePalette( hDC ); hbr = CreateSolidBrush( PALETTEINDEX( BLACK ) ); } FillRect( hDC, &rect, hbr ); DeleteObject( hbr ); return( TRUE ); } // // DrawShadowRect - draw a shaded rectangle around an object // VOID NEAR PASCAL DrawShadowRect( HDC hDC, NPRECT npRect, HPEN hHi, HPEN hLo ) { SelectObject( hDC, hHi ); MoveTo( hDC, npRect->right, npRect->top ); LineTo( hDC, npRect->left, npRect->top ); LineTo( hDC, npRect->left, npRect->bottom ); SelectObject( hDC, hLo ); LineTo( hDC, npRect->right, npRect->bottom ); LineTo( hDC, npRect->right, npRect->top ); } // // NCPaintHyperoid - paint a custom frame // VOID NEAR PASCAL NCPaintHyperoid( HWND hWnd ) { HDC hDC, hDCMem; INT cx, cy, cyCap, h; HPEN hpenHi, hpenLo; HBRUSH hbr; HBITMAP hbm, hbmOld; BITMAP bm; RECT rect; if (IsIconic( hWnd )) return; hDC = GetWindowDC( hWnd ); GetWindowRect( hWnd, &rect ); rect.right -= rect.left, rect.left = 0; rect.bottom -= rect.top, rect.top = 0; cx = GetSystemMetrics( SM_CXFRAME ); cy = GetSystemMetrics( SM_CYFRAME ); cyCap = cy + GetSystemMetrics( SM_CYCAPTION ) - 1; h = rect.bottom - (cyCap + cy); SelectPalette( hDC, hAppPalette, 0 ); RealizePalette( hDC ); if (bBW) { hbr = SelectObject( hDC, CreateSolidBrush( PALETTEINDEX( WHITE ) ) ); hpenHi = hPen[BLACK]; hpenLo = hPen[BLACK]; } else { hbr = SelectObject( hDC, CreateSolidBrush( PALETTEINDEX( GREY ) ) ); hpenHi = hPen[WHITE]; hpenLo = hPen[DKGREY]; } PatBlt( hDC, 0, 0, rect.right, cyCap, PATCOPY ); PatBlt( hDC, 0, rect.bottom - cy, rect.right, rect.bottom, PATCOPY ); PatBlt( hDC, 0, cyCap, cx, h, PATCOPY ); PatBlt( hDC, rect.right - cx, cyCap, cx, h, PATCOPY ); --rect.bottom; --rect.right; DrawShadowRect( hDC, &rect, hpenHi, hpenLo ); --cx; --cy; rect.left += cx; rect.top += cy; rect.right -= cx; rect.bottom -= cy; if (!bBW) DrawShadowRect( hDC, &rect, hpenLo, hpenHi ); // get the title bar rect ++rect.left; ++rect.top; --rect.right; rect.bottom = rect.top + cyCap - (cy + 2); DrawShadowRect( hDC, &rect, hpenHi, hpenLo ); ++rect.right; // for zoom/restore bitmap hDCMem = CreateCompatibleDC( hDC ); hbm = LoadBitmap( NULL, INTRES(OBM_CLOSE) ); GetObject( hbm, sizeof(bm), (LPSTR)&bm ); bm.bmWidth /= 2; // they packed two images in here! hbmOld = SelectObject( hDCMem, hbm ); BitBlt( hDC, rect.left, rect.top, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY ); rect.left += bm.bmWidth; if (IsZoomed( hWnd )) hbm = LoadBitmap( NULL, INTRES(OBM_RESTORE) ); else hbm = LoadBitmap( NULL, INTRES(OBM_ZOOM) ); GetObject( hbm, sizeof(bm), (LPSTR)&bm ); SelectObject( hDCMem, hbm ); rect.right -= bm.bmWidth; BitBlt( hDC, rect.right, rect.top, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY ); hbm = LoadBitmap( NULL, INTRES(OBM_REDUCE) ); GetObject( hbm, sizeof(bm), (LPSTR)&bm ); SelectObject( hDCMem, hbm ); rect.right -= bm.bmWidth; BitBlt( hDC, rect.right, rect.top, bm.bmWidth, bm.bmHeight, hDCMem, 0, 0, SRCCOPY ); --rect.right; DrawShadowRect( hDC, &rect, hpenHi, hpenLo ); // clip the score to the free titlebar area ++rect.left; ++rect.top; rectScoreClip = rect; DeleteObject( SelectObject( hDCMem, hbmOld ) ); DeleteObject( SelectObject( hDC, hbr ) ); DeleteDC( hDCMem ); ReleaseDC( hWnd, hDC ); // make sure the score gets redrawn for (cx = 0; cx < nScoreLen; ++cx) szScore[cx] = '\0'; } // // HyperoidWndProc - the main window proc for Hyperoid // LONG FAR PASCAL EXPORT HyperoidWndProc( HWND hWnd, unsigned message, WORD wParam, LONG lParam ) { switch (message) { case WM_CREATE: RestartHyperoid(); SetTimer( hWnd, DRAW_TIMER, nDrawDelay, NULL ); NCPaintHyperoid( hWnd ); break; case WM_TIMER: switch (wParam) { case DRAW_TIMER: CheckScore( hWnd ); DrawObjects( hWnd ); return( 0 ); case RESTART_TIMER: KillTimer( hWnd, RESTART_TIMER ); bRestart = FALSE; RestartHyperoid(); return( 0 ); } break; case WM_SYSCOMMAND: switch (wParam) { case IDM_NEW: NewGame( hWnd ); break; case IDM_ABOUT: AboutHyperoid( hWnd ); break; default: return( DefWindowProc( hWnd, message, wParam, lParam ) ); } break; case WM_QUERYOPEN: Panic( FALSE ); return( DefWindowProc( hWnd, message, wParam, lParam ) ); case WM_CHAR: if (wParam == VK_ESCAPE) Panic( TRUE ); break; case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSCHAR: if (lParam & (1L<<29)) // alt key is down { return( DefWindowProc( hWnd, message, wParam, lParam ) ); } switch (wParam) { case VK_ESCAPE: if (message == WM_SYSKEYDOWN) Panic( TRUE ); return( 0 ); case VK_SPACE: case VK_TAB: return( 0 ); default: return( DefWindowProc( hWnd, message, wParam, lParam ) ); } break; case WM_ERASEBKGND: return( EraseHyperoidBkgnd( hWnd, (HDC)wParam ) ); case WM_NCACTIVATE: case WM_NCPAINT: NCPaintHyperoid( hWnd ); return( TRUE ); case WM_PAINT: PaintHyperoid( hWnd ); break; case WM_QUERYNEWPALETTE: { HDC hDC = GetDC( hWnd ); SelectPalette( hDC, hAppPalette, 0 ); RealizePalette( hDC ); ReleaseDC( hWnd, hDC ); } return( TRUE ); case WM_DESTROY: KillTimer( hWnd, DRAW_TIMER ); KillTimer( hWnd, RESTART_TIMER ); SaveHyperoidWindowPos( hWnd ); PostQuitMessage( 0 ); break; default: return( DefWindowProc( hWnd, message, wParam, lParam ) ); } return( 0 ); } // // InitHyperoid - initialize everything // BOOL NEAR PASCAL InitHyperoid( VOID ) { DOUBLE dRad; INT nCnt; // allocate the logical palette hAppPalette = CreateHyperoidPalette(); if (!hAppPalette) return( FALSE ); for (nCnt = 0; nCnt < PALETTE_SIZE; ++nCnt) { hPen[nCnt] = CreatePen( PS_SOLID, 1, PALETTEINDEX( nCnt ) ); if (!hPen[nCnt]) return( FALSE ); } for (nCnt = 0; nCnt < IDB_MAX; ++nCnt) { hBitmap[nCnt] = LoadBitmap( hAppInst, INTRES(IDB_blank + nCnt) ); if (!hPen[nCnt]) return( FALSE ); } // seed the randomizer dwSeed = GetCurrentTime(); // create the lookup table (should use resources) for (nCnt = 0; nCnt < DEGREE_SIZE; ++nCnt) { dRad = nCnt * 6.2831855 / DEGREE_SIZE; nCos[nCnt] = (INT)(DEGREE_MAX * cos( dRad )); nSin[nCnt] = (INT)(DEGREE_MAX * sin( dRad )); } // get the initialization file info GetHyperoidIni(); // allocate all objects as free for (nCnt = 0; nCnt < MAX_OBJS; ++nCnt) AddHeadObj( &FreeList, &(Obj[nCnt]) ); // set up the player npPlayer = RemHeadObj( &FreeList ); npPlayer->byPts = DIM(Player); npPlayer->nMass = 256; for (nCnt = 0; nCnt < DIM(Player); ++nCnt) npPlayer->Pts[nCnt] = Player[nCnt]; return( TRUE ); } // // ExitHyperoid - quit the damn game already! // VOID NEAR PASCAL ExitHyperoid( VOID ) { INT nCnt; if (hAppPalette) DeleteObject( hAppPalette ); for (nCnt = 0; nCnt < PALETTE_SIZE; ++nCnt) if (hPen[nCnt]) DeleteObject( hPen[nCnt] ); for (nCnt = 0; nCnt < IDB_MAX; ++nCnt) if (hBitmap[nCnt]) DeleteObject( hBitmap[nCnt] ); } // // WinMain - everybody has to have one // INT FAR PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, INT nCmdShow ) { MSG msg; hAppInst = hInstance; if (!hPrevInstance) { // create the class if we're first if (!CreateHyperoidClass()) return( FALSE ); } else { // Copy data from previous instance GetInstanceData( hPrevInstance, (PSTR)szAppName, sizeof(szAppName) ); } if (!InitHyperoid()) goto Abort; // I LOVE GOTOS! REALLY I DO! hAppWnd = CreateHyperoidWindow( lpszCmdLine, nCmdShow ); if (!hAppWnd) return( FALSE ); while (GetMessage( &msg, NULL, 0, 0 )) { TranslateMessage( &msg ); DispatchMessage( &msg ); } Abort: ExitHyperoid(); return( msg.wParam ); }