// | |
// 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 ); | |
} |