FFDONUTS.C

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
*
* File: ffdonuts.c
* Content: DirectInput ForceFeedback sample
*
*
***************************************************************************/

#define INITGUID

#include "ffdonuts.h"


LPDIRECTDRAWSURFACE lpFrontBuffer;
LPDIRECTDRAWSURFACE lpBackBuffer;
LPDIRECTDRAWSURFACE lpDonut;
LPDIRECTDRAWSURFACE lpPyramid;
LPDIRECTDRAWSURFACE lpCube;
LPDIRECTDRAWSURFACE lpSphere;
LPDIRECTDRAWSURFACE lpShip;
LPDIRECTDRAWSURFACE lpNum;
LPDIRECTDRAW lpDD;
LPDIRECTDRAWPALETTE lpArtPalette;
LPDIRECTDRAWPALETTE lpSplashPalette;
BOOL bPlayIdle = FALSE;
BOOL bPlayBuzz = FALSE;
BOOL bPlayRev = FALSE;
DWORD lastInput = 0;
BOOL lastThrust = FALSE;
BOOL lastShield = FALSE;
int showDelay = 0;
HWND hWndMain;
BOOL bShowFrameCount=TRUE;
BOOL bIsActive;
BOOL bMouseVisible;
DWORD dwFrameCount;
DWORD dwFrameTime;
DWORD dwFrames;
DWORD dwFramesLast;
BOOL bUseEmulation;
BOOL bTest=FALSE;
BOOL bStress=FALSE;
DWORD dwTransType;
RGBQUAD SPalette[256];
DWORD lastTickCount;
int score;
int ProgramState;
int level;
int restCount;
DWORD dwFillColor;
BOOL bSpecialEffects = FALSE;
DWORD ShowLevelCount = 3000;
DWORD ScreenX;
DWORD ScreenY;
DWORD ScreenBpp;
#ifdef USE_DSOUND
BOOL bWantSound = TRUE; //global hack to turn off sound
BOOL bSoundEnabled = FALSE;
#endif

int getint(char**p, int def);

#ifdef DEBUG
char DebugBuf[256];
BOOL bHELBlt = FALSE;
#endif

DBLNODE DL; // Display List

#ifdef USE_DSOUND
LPDIRECTSOUND lpDS;
HSNDOBJ hsoBeginLevel = NULL;
HSNDOBJ hsoEngineIdle = NULL;
HSNDOBJ hsoEngineRev = NULL;
HSNDOBJ hsoSkidToStop = NULL;
HSNDOBJ hsoShieldBuzz = NULL;
HSNDOBJ hsoShipExplode = NULL;
HSNDOBJ hsoFireBullet = NULL;
HSNDOBJ hsoShipBounce = NULL;
HSNDOBJ hsoDonutExplode = NULL;
HSNDOBJ hsoPyramidExplode = NULL;
HSNDOBJ hsoCubeExplode = NULL;
HSNDOBJ hsoSphereExplode = NULL;
#endif

void setup_game(void)
{
restCount = GetTickCount();
initLevel( ++level );
// set the palette
lpFrontBuffer->lpVtbl->SetPalette( lpFrontBuffer, lpArtPalette );
}

/*
* MainWndproc
*
* Callback for all Windows messages
*/
long FAR PASCAL MainWndproc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_ACTIVATEAPP:
bIsActive = (BOOL) wParam;
if( bIsActive )
{
bMouseVisible = FALSE;
lastTickCount = GetTickCount();
bSpecialEffects = FALSE;
// we have just been reactivated...
// time to acquire our device(s) again
inputAcquireDevices();
}
else
{
bMouseVisible = TRUE;
}
break;

case WM_CREATE:
break;

case WM_SETCURSOR:
if( !bMouseVisible )
{
SetCursor(NULL);
}
else
{
SetCursor(LoadCursor( NULL, IDC_ARROW ));
}
return TRUE;

case WM_KEYDOWN:
switch( wParam )
{
case VK_F3:
#ifdef USE_DSOUND
if(bWantSound)
{
if( bSoundEnabled )
{
DestroySound();
}
else
{
InitializeSound();
}
}
#endif
break;
case VK_F5:
bShowFrameCount = !bShowFrameCount;
if( bShowFrameCount )
{
dwFrameCount = 0;
dwFrameTime = timeGetTime();
}
break;
case VK_RETURN:
if( ProgramState == PS_SPLASH )
{
ProgramState = PS_BEGINREST;
setup_game();
}
break;
case VK_ESCAPE:
case VK_F12:
PostMessage( hWnd, WM_CLOSE, 0, 0 );
return 0;
case VK_F1:
bSpecialEffects = !bSpecialEffects;
break;
}
break;


case WM_KEYUP:
switch( wParam )
{
case VK_NUMPAD7:
lastInput &= ~KEY_SHIELD;
break;
case VK_NUMPAD5:
lastInput &= ~KEY_STOP;
break;
case VK_DOWN:
case VK_NUMPAD2:
lastInput &= ~KEY_DOWN;
break;
case VK_LEFT:
case VK_NUMPAD4:
lastInput &= ~KEY_LEFT;
break;
case VK_RIGHT:
case VK_NUMPAD6:
lastInput &= ~KEY_RIGHT;
break;
case VK_UP:
case VK_NUMPAD8:
lastInput &= ~KEY_UP;
break;
case VK_NUMPAD3:
lastInput &= ~KEY_THROW;
break;
case VK_SPACE:
lastInput &= ~KEY_FIRE;
break;
}
break;

case WM_ERASEBKGND:
return 1;

case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
return 1;

case WM_DESTROY:
lastInput=0;
DestroyGame();
inputCleanupDirectInput();
PostQuitMessage( 0 );
break;

default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);

} /* MainWndproc */

/*
* initApplication
*
* Do that Windows initialization stuff...
*/
static BOOL initApplication( HANDLE hInstance, int nCmdShow )
{
WNDCLASS wc;
BOOL rc;

wc.style = CS_DBLCLKS;
wc.lpfnWndProc = MainWndproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( hInstance, "DONUTS_ICON");
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = GetStockObject( BLACK_BRUSH );
wc.lpszMenuName = NULL;
wc.lpszClassName = "DonutsClass";
rc = RegisterClass( &wc );
if( !rc )
{
return FALSE;
}

hWndMain = CreateWindowEx(0, // WS_EX_TOPMOST,
"DonutsClass",
"Donuts",
WS_VISIBLE | // so we don't have to call ShowWindow
WS_POPUP | // non-app window
WS_SYSMENU, // so we get an icon in the tray
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL );

if( !hWndMain )
{
return FALSE;
}

UpdateWindow( hWndMain );

return TRUE;

} /* initApplication */

/*
* WinMain
*/
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow )
{
MSG msg;

while( lpCmdLine[0] == '-' )
{
lpCmdLine++;

switch (*lpCmdLine++)
{
case 'e':
bUseEmulation = TRUE;
break;
case 't':
bTest = TRUE;
break;
case 'x':
bStress= TRUE;
bTest = TRUE;
break;
}
while( IS_SPACE(*lpCmdLine) )
{
lpCmdLine++;
}
}

ScreenX = getint(&lpCmdLine, 640);
ScreenY = getint(&lpCmdLine, 480);
ScreenBpp = getint(&lpCmdLine, 8);

if( !initApplication(hInstance, nCmdShow) )
{
return FALSE;
}

// initialize DirectInput functionality
if( !inputInitDirectInput(hInstance, hWndMain) )
{
inputCleanupDirectInput();
DestroyWindow( hWndMain );
return FALSE;
}

if( !InitializeGame() )
{
DestroyWindow( hWndMain );
return FALSE;
}

dwFrameTime = timeGetTime();

while( 1 )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( !GetMessage( &msg, NULL, 0, 0 ) )
{
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else if ( bIsActive )
{
UpdateFrame();
}
else
{
WaitMessage();
}
}
} /* WinMain */


void DestroyGame( void )
{
}

BOOL InitializeGame( void )
{
DDCAPS ddcaps;
HRESULT ddrval;
DDSURFACEDESC ddsd;
DDSCAPS ddscaps;
#ifdef NT_HACK
DDSURFACEDESC DDSurfDesc;
#endif

score = 0;
if( bTest )
ShowLevelCount = 1000;

if( bUseEmulation )
ddrval = DirectDrawCreate( (LPVOID) DDCREATE_EMULATIONONLY, &lpDD, NULL );
else
ddrval = DirectDrawCreate( NULL, &lpDD, NULL );

if( ddrval != DD_OK )
return CleanupAndExit("DirectDrawCreate Failed!");

ddrval = lpDD->lpVtbl->SetCooperativeLevel( lpDD, hWndMain,
DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
if( ddrval != DD_OK )
return CleanupAndExit("SetCooperativeLevel Failed");

#ifdef NT_HACK
DDSurfDesc.dwSize = sizeof(DDSurfDesc);
ddrval = lpDD->lpVtbl->GetDisplayMode(lpDD,&DDSurfDesc);
if(ddrval == DD_OK)
ScreenBpp = DDSurfDesc.ddpfPixelFormat.dwRGBBitCount;
#endif

// set the mode
ddrval = lpDD->lpVtbl->SetDisplayMode( lpDD, ScreenX, ScreenY, ScreenBpp );
if( ddrval != DD_OK )
return CleanupAndExit("SetDisplayMode Failed!");

// check the color key hardware capabilities
dwTransType = DDBLTFAST_SRCCOLORKEY;
ddcaps.dwSize = sizeof( ddcaps );

#ifdef DEBUG
if( GetProfileInt( "Donuts", "force_dest_blt", 0) )
{
dwTransType = DDBLTFAST_DESTCOLORKEY;
}
bHELBlt = GetProfileInt( "Donuts", "force_HEL_blt", bHELBlt );
#endif

// Create surfaces
memset( &ddsd, 0, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpFrontBuffer, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface FrontBuffer Failed!");

// get a pointer to the back buffer
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpFrontBuffer->lpVtbl->GetAttachedSurface(
lpFrontBuffer,
&ddscaps,
&lpBackBuffer );
if( ddrval != DD_OK )
return CleanupAndExit("GetAttachedDurface Failed!");

ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
#ifdef DEBUG
if( bHELBlt )
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
#endif
ddsd.dwWidth = 320;
ddsd.dwHeight = 384;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpDonut, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpDonut Failed!");

ddsd.dwHeight = 128;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpPyramid, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpPyramid Failed!");

ddsd.dwHeight = 32;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpCube, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpCube Failed!");

ddsd.dwHeight = 32;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpSphere, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpSphere Failed!");
// Set the background color fill color

ddsd.dwHeight = 256;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpShip, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpShip Failed!");

ddsd.dwHeight = 16;
ddrval = lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpNum, NULL );
if( ddrval != DD_OK )
return CleanupAndExit("CreateSurface lpNum Failed!");

if( !RestoreSurfaces() )
return CleanupAndExit("RestoreSurfaces Failed!");

DL.next = DL.prev = &DL; // null display list
DL.type = OBJ_SHIP;
DL.surf = lpShip;
lastTickCount = GetTickCount();

#ifdef USE_DSOUND
if(bWantSound)
{
InitializeSound();
}
#endif

if(bTest)
{
ProgramState = PS_ACTIVE;
setup_game();
}
else
{
ProgramState = PS_SPLASH;
}
return TRUE;
}

BOOL CleanupAndExit( char *err)
{
#ifdef DEBUG
wsprintf(DebugBuf, "___CleanupAndExit err = %s\n", err );
OutputDebugString( DebugBuf );
#endif

// make the cursor visible
SetCursor(LoadCursor( NULL, IDC_ARROW ));
bMouseVisible = TRUE;

if( lpDonut != NULL )
lpDonut->lpVtbl->Release( lpDonut );

if( lpPyramid != NULL )
lpPyramid->lpVtbl->Release( lpPyramid );

if( lpCube != NULL )
lpCube->lpVtbl->Release( lpCube );

if( lpSphere != NULL )
lpSphere->lpVtbl->Release( lpSphere );

if( lpShip != NULL )
lpShip->lpVtbl->Release( lpShip );

if( lpNum != NULL )
lpNum->lpVtbl->Release( lpNum );

if( lpFrontBuffer != NULL )
lpFrontBuffer->lpVtbl->Release( lpFrontBuffer );

if( lpArtPalette != NULL )
lpArtPalette->lpVtbl->Release( lpArtPalette );

if( lpSplashPalette != NULL )
lpSplashPalette->lpVtbl->Release( lpSplashPalette );

if( lpDD != NULL )
lpDD->lpVtbl->Release( lpDD );

//
// warn user if there is one
//

if( !bStress )
{
MessageBox( hWndMain, err, "ERROR", MB_OK );
}
return FALSE;
}

void bltSplash( void )
{
HRESULT ddrval;
HBITMAP hbm;

// set the palette before loading the splash screen
lpFrontBuffer->lpVtbl->SetPalette( lpFrontBuffer, lpSplashPalette );

hbm = (HBITMAP)LoadImage( GetModuleHandle( NULL ), "SPLASH", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );
if ( NULL == hbm )
return;

// if the surface is lost, DDCopyBitmap will fail and the surface will
// be restored in FlipScreen.
ddrval = DDCopyBitmap( lpBackBuffer, hbm, 0, 0, 0, 0 );

DeleteObject( hbm );

FlipScreen();
}


#ifdef USE_DSOUND
//
// play a sound, but first set the panning according to where the
// object is on the screen. fake 3D sound.
//
void playPanned(HSNDOBJ hSO, DBLNODE *object)
{
IDirectSoundBuffer *pDSB = SndObjGetFreeBuffer(hSO);

if(!bWantSound)
return; // No sound our Work is done

if (pDSB)
{
switch(ScreenX)
{
case 320:
IDirectSoundBuffer_SetPan(pDSB, (LONG)((20000.0 *
((object->dst.right + object->dst.left) / 2) / 320.0) - 10000.0));
break;
case 640:
IDirectSoundBuffer_SetPan(pDSB, (LONG)((20000.0 *
((object->dst.right + object->dst.left) / 2) / 640.0) - 10000.0));
break;
case 1024:
IDirectSoundBuffer_SetPan(pDSB, (LONG)((20000.0 *
((object->dst.right + object->dst.left) / 2) / 1024.0) - 10000.0));
break;
case 1280:
IDirectSoundBuffer_SetPan(pDSB, (LONG)((20000.0 *
((object->dst.right + object->dst.left) / 2) / 1280.0) - 10000.0));
break;
}

IDirectSoundBuffer_Play(pDSB, 0, 0, 0);
}
}
#endif


void UpdateFrame( void )
{
switch( ProgramState )
{
case PS_SPLASH:
// display the splash screen
bltSplash();
return;
case PS_ACTIVE:
UpdateDisplayList();
CheckForHits();
DrawDisplayList();
if ( isDisplayListEmpty() )
{
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjStop(hsoEngineIdle);
SndObjStop(hsoEngineRev);
}
#endif
bPlayIdle = FALSE;
bPlayRev = FALSE;
lastThrust = lastShield = FALSE;
ProgramState = PS_BEGINREST;
restCount = GetTickCount();
initLevel( ++level );
}
return;
case PS_BEGINREST:
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjPlay(hsoBeginLevel, 0);
}
#endif
ProgramState = PS_REST;
//
// FALLTHRU
//
case PS_REST:
if( ( GetTickCount() - restCount ) > ShowLevelCount )
{
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjPlay(hsoEngineIdle, DSBPLAY_LOOPING);
}
#endif
bPlayIdle = TRUE;
lastTickCount = GetTickCount();
ProgramState = PS_ACTIVE;
}
else
{
DisplayLevel();
}
return;
}
}

void DisplayLevel( void )
{
char buf[10];

EraseScreen();
buf[0] = 10 + '0';
buf[1] = 11 + '0';
buf[2] = 12 + '0';
buf[3] = 11 + '0';
buf[4] = 10 + '0';
buf[5] = '\0';
bltScore( buf, ScreenX/2-64, ScreenY/2-8 );
buf[0] = level / 100 + '0';
buf[1] = level / 10 + '0';
buf[2] = level % 10 + '0';
buf[3] = '\0';
bltScore( buf, ScreenX/2+22, ScreenY/2-8 );
FlipScreen();
}

void bltScore( char *num, int x, int y )
{
char *c;
RECT src;
int i;
HRESULT ddrval;

for(c=num; *c != '\0'; c++)
{
while( 1 )
{
i = *c - '0';
src.left = i*16;
src.top = 0;
src.right = src.left + 16;
src.bottom = src.top + 16;
ddrval = lpBackBuffer->lpVtbl->BltFast( lpBackBuffer, x, y, lpNum, &src, dwTransType );
if( ddrval == DD_OK )
{
break;
}
if( ddrval == DDERR_SURFACELOST )
{
if( !RestoreSurfaces() )
return;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return;
}
}
x += 16;
}
}

void CheckForHits( void )
{
LPDBLNODE bullet, target, save;
int frame, x, y, l, t;
BOOL hit;

// update screen rects
target = &DL;
do
{
frame = (DWORD)target->frame;
switch( target->type )
{
case OBJ_DONUT:
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 64;
target->dst.bottom = target->dst.top + 64;
target->src.left = 64 * (frame % 5);
target->src.top = 64 * (frame /5);
target->src.right = target->src.left + 64;
target->src.bottom = target->src.top + 64;
break;
case OBJ_PYRAMID:
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 32;
target->dst.bottom = target->dst.top + 32;
target->src.left = 32 * (frame % 10);
target->src.top = 32 * (frame /10);
target->src.right = target->src.left + 32;
target->src.bottom = target->src.top + 32;
break;
case OBJ_SPHERE:
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 16;
target->dst.bottom = target->dst.top + 16;
target->src.left = 16 * (frame % 20);
target->src.top = 16 * (frame /20);
target->src.right = target->src.left + 16;
target->src.bottom = target->src.top + 16;
break;
case OBJ_CUBE:
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 16;
target->dst.bottom = target->dst.top + 16;
target->src.left = 16 * (frame % 20);
target->src.top = 16 * (frame /20);
target->src.right = target->src.left + 16;
target->src.bottom = target->src.top + 16;
break;
case OBJ_SHIP:
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 32;
target->dst.bottom = target->dst.top + 32;
if( lastShield )
target->src.top = 32 * (frame / 10) + 128;
else
target->src.top = 32 * (frame /10);
target->src.left = 32 * (frame % 10);
target->src.right = target->src.left + 32;
target->src.bottom = target->src.top + 32;
break;
case OBJ_BULLET:
frame = (DWORD)target->frame/20 % 4;
target->dst.left = (DWORD)target->posx;
target->dst.top = (DWORD)target->posy;
target->dst.right = target->dst.left + 3;
target->dst.bottom = target->dst.top + 3;
target->src.left = BULLET_X + frame*4;
target->src.top = BULLET_Y;
target->src.right = target->src.left + 3;
target->src.bottom = target->src.top + 3;
break;
}
target = target->next;
}
while( target != &DL );

bullet=&DL;
do
{
hit = FALSE;
if((bullet->type != OBJ_BULLET) && (bullet != &DL))
{
bullet = bullet->next;
continue;
}

x = (bullet->dst.left + bullet->dst.right) / 2;
y = (bullet->dst.top + bullet->dst.bottom) / 2;
for(target=DL.next; target != &DL; target = target->next)
{
if( ( target->type != OBJ_DONUT ) &&
( target->type != OBJ_PYRAMID ) &&
( target->type != OBJ_SPHERE ) &&
( target->type != OBJ_CUBE ) )
continue;

if( (x >= target->dst.left) &&
(x < target->dst.right) &&
(y >= target->dst.top) &&
(y < target->dst.bottom) )
{
if ((bullet != &DL) || !lastShield)
{
// the bullet hit the target
switch( target->type )
{
case OBJ_DONUT:
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoDonutExplode, target);
}
#endif
addObject( OBJ_PYRAMID, target->dst.left,
target->dst.top, -1.0, -1.0 );
addObject( OBJ_PYRAMID, target->dst.left,
target->dst.top, -1.0, -1.0 );
addObject( OBJ_PYRAMID, target->dst.left,
target->dst.top, -1.0, -1.0 );
score += 10;
break;
case OBJ_PYRAMID:
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoPyramidExplode, target);
}
#endif
addObject( OBJ_SPHERE, target->dst.left,
target->dst.top, -1.0, -1.0 );
addObject( OBJ_CUBE, target->dst.left,
target->dst.top, -1.0, -1.0 );
addObject( OBJ_CUBE, target->dst.left,
target->dst.top, -1.0, -1.0 );
score += 20;
break;
case OBJ_CUBE:
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoCubeExplode, target);
}
#endif
addObject( OBJ_SPHERE, target->dst.left,
target->dst.top, -1.0, -1.0 );
addObject( OBJ_SPHERE, target->dst.left,
target->dst.top, -1.0, -1.0 );
break;
score += 40;
case OBJ_SPHERE:
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoSphereExplode, target);
}
#endif
score += 20;
}

l = target->dst.left;
t = target->dst.top;
DeleteFromList( target );
}

hit = TRUE;
}

if( hit )
{
if( bullet == &DL )
{
hit = FALSE;
if (!lastShield && !showDelay && !bTest)
{
// ship has exploded
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoShipExplode, bullet);
}
#endif
inputPlayEffect(EF_EXPLODE, 0);

score -= 150;
if (score < 0)
score = 0;

addObject( OBJ_SPHERE, l, t, -1.0, -1.0 );
addObject( OBJ_SPHERE, l, t, -1.0, -1.0 );
addObject( OBJ_SPHERE, l, t, -1.0, -1.0 );
addObject( OBJ_SPHERE, l, t, -1.0, -1.0 );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
addObject( OBJ_BULLET, l, t,
randDouble( -0.5, 0.5 ), randDouble( -0.5, 0.5 ) );
initShip(TRUE);
}
}

break;
}
}


if( hit )
{
save = bullet;
bullet = bullet->next;

DeleteFromList( save );
}
else
{
bullet = bullet->next;
}

} while (bullet != &DL);
}

void EraseScreen( void )
{
DDBLTFX ddbltfx;
HRESULT ddrval;

if( bSpecialEffects ) // cool looking screen with no colorfill
return;

// Erase the background
ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwFillColor = dwFillColor;
while( 1 )
{
ddrval = lpBackBuffer->lpVtbl->Blt( lpBackBuffer, NULL, NULL,
NULL, DDBLT_COLORFILL, &ddbltfx );

if( ddrval == DD_OK )
{
break;
}
if( ddrval == DDERR_SURFACELOST )
{
if( !RestoreSurfaces() )
return;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return;
}
}
}

void FlipScreen( void )
{
HRESULT ddrval;

// Flip the surfaces
while( 1 )
{
ddrval = lpFrontBuffer->lpVtbl->Flip( lpFrontBuffer, NULL, 0 );
if( ddrval == DD_OK )
{
break;
}
if( ddrval == DDERR_SURFACELOST )
{
if( !RestoreSurfaces() )
{
return;
}
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
break;
}
}
}

void DrawDisplayList( void )
{
LPDBLNODE this;
LPDBLNODE last;
HRESULT ddrval;
char scorebuf[11];
int rem;

// blt everything in reverse order if we are doing destination transparency
// calculate score string
scorebuf[0] = score/10000000 + '0';
rem = score % 10000000;
scorebuf[1] = rem/1000000 + '0';
rem = score % 1000000;
scorebuf[2] = rem/100000 + '0';
rem = score % 100000;
scorebuf[3] = rem/10000 + '0';
rem = score % 10000;
scorebuf[4] = rem/1000 + '0';
rem = score % 1000;
scorebuf[5] = rem/100 + '0';
rem = score % 100;
scorebuf[6] = rem/10 + '0';
rem = score % 10;
scorebuf[7] = rem + '0';
#ifdef USE_DSOUND
if( bSoundEnabled )
{
scorebuf[8] = 14 + '0';
scorebuf[9] = 13 + '0';
scorebuf[10] = '\0';
}
else
#endif
{
scorebuf[8] = '\0';
}

EraseScreen();
if( dwTransType == DDBLTFAST_DESTCOLORKEY )
{
bltScore(scorebuf, 10, ScreenY-26);

if( bShowFrameCount )
DisplayFrameRate();

this = DL.next; // start with the topmost bitmap
last = DL.next; // don't blt it twice

if (showDelay)
last = &DL;
}
else
{
this = &DL; // start with the bottommost bitmap (the ship)
last = &DL; // don't blt it twice

if (showDelay)
this = this->prev;
}

do
{
while( 1 )
{
ddrval = lpBackBuffer->lpVtbl->BltFast( lpBackBuffer, this->dst.left, this->dst.top, this->surf, &(this->src), dwTransType );
if( ddrval == DD_OK )
{
break;
}
if( ddrval == DDERR_SURFACELOST )
{
if( !RestoreSurfaces() )
return;
}
if( ddrval != DDERR_WASSTILLDRAWING )
{
return;
}
}
if( dwTransType != DDBLTFAST_DESTCOLORKEY )
{
this = this->prev;
}
else
{
this = this->next;
}
}
while( this != last );

if( dwTransType != DDBLTFAST_DESTCOLORKEY )
{
bltScore(scorebuf, 10, ScreenY-26);

if( bShowFrameCount )
DisplayFrameRate();
}

FlipScreen();
}

void DisplayFrameRate( void )
{
DWORD time2;
char buff[256];

dwFrameCount++;
time2 = timeGetTime() - dwFrameTime;
if( time2 > 1000 )
{
dwFrames = (dwFrameCount*1000)/time2;
dwFrameTime = timeGetTime();
dwFrameCount = 0;
}
if( dwFrames == 0 )
{
return;
}

if (dwFrames != dwFramesLast)
{
dwFramesLast = dwFrames;
}

if( dwFrames > 99 )
{
dwFrames = 99;
}
buff[0] = (char)((dwFrames / 10) + '0');
buff[1] = (char)((dwFrames % 10) + '0');
buff[2] = '\0';
bltScore(buff, ScreenX/2-25, 10);
}

void DeleteFromList( LPDBLNODE this )
{
this->next->prev = this->prev;
this->prev->next = this->next;
LocalFree( this );
}

void UpdateDisplayList( void )
{
LPDBLNODE this;
LPDBLNODE save;
DWORD thisTickCount = GetTickCount();
DWORD tickDiff = thisTickCount - lastTickCount;
double maxx, maxy;
double maxframe;
DWORD input = lastInput;
BOOL event = FALSE;
BOOL fBounce = FALSE;
LONG lAngle = 0;

if( bTest )
{
input |= (KEY_RIGHT | KEY_FIRE);
}
lastTickCount = thisTickCount;

// get the state of the input device
input = inputProcessDeviceInput();

if (showDelay)
{
showDelay -= (int)tickDiff;
if (showDelay < 0)
{
showDelay = 0;
lastShield = FALSE;
initShip( FALSE );
}
}

// update the ship
if( !showDelay )
{
DL.posx += DL.velx * (double)tickDiff;
DL.posy += DL.vely * (double)tickDiff;
}
if( DL.posx > MAX_SHIP_X )
{
DL.posx = MAX_SHIP_X;
DL.velx = -DL.velx;
event = TRUE;
// set the bounce angle
// (bouncing off the right edge of the screen)
fBounce = TRUE;
lAngle = 90;
}
else if ( DL.posx < 0 )
{
DL.posx =0;
DL.velx = -DL.velx;
event = TRUE;
// set the bounce angle
// (bouncing off the left edge of the screen)
fBounce = TRUE;
lAngle = 270;
}
if( DL.posy > MAX_SHIP_Y )
{
DL.posy = MAX_SHIP_Y;
DL.vely = -DL.vely;
event = TRUE;
// set the bounce angle
// (bouncing off the bottom edge of the screen)
fBounce = TRUE;
lAngle = 180;
}
else if ( DL.posy < 0 )
{
DL.posy =0;
DL.vely = -DL.vely;
event = TRUE;
// set the bounce angle
// (bouncing off the top edge of the screen)
fBounce = TRUE;
lAngle = 0;
}

if (event)
{
#ifdef USE_DSOUND
if(bWantSound)
{
playPanned(hsoShipBounce, &DL);
}
#endif
event = FALSE;
}

if(fBounce)
{
// "bounce" the ship
inputPlayEffect(EF_BOUNCE, lAngle);
}

if ((event = (showDelay || ((input & KEY_SHIELD) == KEY_SHIELD))) !=
lastShield)
{
if (event && !showDelay)
{
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjPlay(hsoShieldBuzz, DSBPLAY_LOOPING);
}
#endif
bPlayBuzz = TRUE;
}
else
{
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjStop(hsoShieldBuzz);
}
#endif
bPlayBuzz = FALSE;
}
lastShield = event;
}
if (event)
{
input &= ~(KEY_FIRE);
}

if (input & KEY_FIRE)
{
if( !showDelay )
{
// add a bullet to the scene
score--;
if(score < 0)
score = 0;

#ifdef USE_DSOUND
if(bWantSound)
{
SndObjPlay(hsoFireBullet, 0);
}
#endif

// play the "fire" effect
inputPlayEffect(EF_FIRE, 0);

addObject( OBJ_BULLET, Dirx[(int)DL.frame]*6.0 + 16.0 + DL.posx,
Diry[(int)DL.frame]*6.0 + 16.0 + DL.posy,
Dirx[(int)DL.frame]*500.0/1000.0,
Diry[(int)DL.frame]*500.0/1000.0 );
}
}

event = FALSE;
if( input & KEY_LEFT )
{
DL.frame -= 1.0;
if( DL.frame < 0.0 )
DL.frame += MAX_SHIP_FRAME;
}
if( input & KEY_RIGHT )
{
DL.frame += 1.0;
if( DL.frame >= MAX_SHIP_FRAME)
DL.frame -= MAX_SHIP_FRAME;
}
if( input & KEY_UP )
{
DL.velx += Dirx[(int)DL.frame] * 10.0/1000.0;
DL.vely += Diry[(int)DL.frame] * 10.0/1000.0;
event = TRUE;
}
if( input & KEY_DOWN )
{
DL.velx -= Dirx[(int)DL.frame] * 10.0/1000.0;
DL.vely -= Diry[(int)DL.frame] * 10.0/1000.0;
event = TRUE;
}

if (event != lastThrust)
{
if (event)
{
input &= ~KEY_STOP;
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjStop(hsoSkidToStop);
SndObjPlay(hsoEngineRev, DSBPLAY_LOOPING);
}
#endif
bPlayRev = TRUE;
}
else
{
#ifdef USE_DSOUND
if(bWantSound)
{
SndObjStop(hsoEngineRev);
}
#endif
bPlayRev = FALSE;
}

lastThrust = event;
}

if( input & KEY_STOP )
{
#ifdef USE_DSOUND
if(bWantSound)
{
if (DL.velx || DL.vely)
{
playPanned(hsoSkidToStop, &DL);
}
}
#endif
DL.velx = 0;
DL.vely = 0;
}

this = DL.next;
do
{
this->posx += this->velx * (double)tickDiff;
this->posy += this->vely * (double)tickDiff;
this->frame += this->delay * (double)tickDiff;
switch( this->type )
{
case OBJ_DONUT:
maxx = (double)MAX_DONUT_X;
maxy = (double)MAX_DONUT_Y;
maxframe = (double)MAX_DONUT_FRAME;
break;
case OBJ_PYRAMID:
maxx = (double)MAX_PYRAMID_X;
maxy = (double)MAX_PYRAMID_Y;
maxframe = (double)MAX_PYRAMID_FRAME;
break;
case OBJ_SPHERE:
maxx = (double)MAX_SPHERE_X;
maxy = (double)MAX_SPHERE_Y;
maxframe = (double)MAX_SPHERE_FRAME;
break;
case OBJ_CUBE:
maxx = (double)MAX_CUBE_X;
maxy = (double)MAX_CUBE_Y;
maxframe = (double)MAX_CUBE_FRAME;
break;
case OBJ_BULLET:
maxx = (double)MAX_BULLET_X;
maxy = (double)MAX_BULLET_Y;
maxframe = (double)MAX_BULLET_FRAME;
if( this->frame >= (double)MAX_BULLET_FRAME )
{
save = this;
this = this->next;
DeleteFromList( save );
continue;
}
break;
}
if( this != &DL )
{
if( this->posx > maxx )
{
this->posx = maxx;
this->velx = -this->velx;
}
else if ( this->posx < 0 )
{
this->posx =0;
this->velx = -this->velx;
}
if( this->posy > maxy )
{
this->posy = maxy;
this->vely = -this->vely;
}
else if ( this->posy < 0 )
{
this->posy =0;
this->vely = -this->vely;
}
if( this->frame >= maxframe )
{
this->frame -= maxframe;
}
this = this->next;
}
}
while( this != &DL );
}

BOOL isDisplayListEmpty( void )
{
LPDBLNODE ptr;

for(ptr=DL.next; ptr != &DL; ptr = ptr->next)
{
if(ptr->type != OBJ_BULLET)
return FALSE;
}
return TRUE;
}

void initShip( BOOL delay )
{
DL.posx = (double)(ScreenX/2-16); // center the ship
DL.posy = (double)(ScreenY/2-16);
DL.frame = 0.0;
if( bTest )
{
DL.velx = 0.25;
DL.vely = 0.5;
}
else
{
DL.velx = DL.vely = 0.0; // not moving
}
if( !bTest && delay )
showDelay = DEF_SHOW_DELAY;
}

void initLevel( int level )
{
int i;

// clear any stray bullets out of the display list
while( DL.next != &DL )
{
DeleteFromList( DL.next );
}
for(i=0; i<(2*level-1); i++)
{
addObject( OBJ_DONUT, -1.0, -1.0, -1.0, -1.0 );
}
initShip(TRUE);
}

void addObject( SHORT type, double x, double y, double vx, double vy )
{
LPDBLNODE new;

new = (LPDBLNODE) LocalAlloc( LPTR, sizeof( DBLNODE ) );
if( new == NULL)
return;

new->type = type;
switch( type )
{
case OBJ_DONUT:
if( x < 0.0) // no position specified?
{
new->posx = randDouble( 0.0, (double)MAX_DONUT_X );
new->posy = randDouble( 0.0, (double)MAX_DONUT_Y );
}
else
{
new->posx = x;
new->posy = y;
}
new->velx = randDouble( -50.0/1000.0, 50.0/1000.0 );
new->vely = randDouble( -50.0/1000.0, 50.0/1000.0 );
new->frame = randDouble( 0, 30 );
new->delay = 30.0*randDouble( 0.1, 0.4 )/1000.0;
new->surf = lpDonut;
linkObject( new );
break;
case OBJ_PYRAMID:
if( x < 0) // no position specified?
{
new->posx = randDouble( 0.0, (double)MAX_PYRAMID_X );
new->posy = randDouble( 0.0, (double)MAX_PYRAMID_Y );
}
else
{
new->posx = x;
new->posy = y;
}
new->velx = 1.5*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->vely = 1.5*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->frame = randDouble( 0, 30 );
new->delay = 40.0*randDouble( 0.3, 1.0 )/1000.0;
new->surf = lpPyramid;
linkObject( new );
break;
case OBJ_SPHERE:
if( x < 0) // no position specified?
{
new->posx = randDouble( 0.0, (double)MAX_SPHERE_X );
new->posy = randDouble( 0.0, (double)MAX_SPHERE_Y );
}
else
{
new->posx = x;
new->posy = y;
}
new->velx = 3.0*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->vely = 3.0*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->frame = randDouble( 0, 30 );
new->delay = 40.0*randDouble( 1.5, 2.0 )/1000.0;
new->surf = lpSphere;
linkObject( new );
break;
case OBJ_CUBE:
if( x < 0) // no position specified?
{
new->posx = randDouble( 0.0, (double)MAX_CUBE_X );
new->posy = randDouble( 0.0, (double)MAX_CUBE_Y );
}
else
{
new->posx = x;
new->posy = y;
}
new->velx = 4.0*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->vely = 4.0*randDouble( -50.0/1000.0, 50.0/1000.0 );
new->frame = randDouble( 0, 30 );
new->delay = 40.0*randDouble( 0.8, 2.0 )/1000.0;
new->surf = lpCube;
linkObject( new );
break;
case OBJ_BULLET:
new->posx = x;
new->posy = y;
new->velx = vx;
new->vely = vy;
new->frame = 0.0;
new->delay = 1.0;
new->surf = lpNum;
linkObject( new );
break;
}
}

void linkObject( LPDBLNODE new )
{
new->next = DL.next;
new->prev = &DL;
DL.next->prev = new;
DL.next = new;
}

void linkLastObject( LPDBLNODE new )
{
new->prev = DL.prev;
new->next = &DL;
DL.prev->next = new;
DL.prev = new;
}


BOOL RestoreSurfaces( void )
{
HRESULT ddrval;
HBITMAP hbm;

ddrval = lpFrontBuffer->lpVtbl->Restore(lpFrontBuffer);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpDonut->lpVtbl->Restore(lpDonut);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpPyramid->lpVtbl->Restore(lpPyramid);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpCube->lpVtbl->Restore(lpCube);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpSphere->lpVtbl->Restore(lpSphere);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpShip->lpVtbl->Restore(lpShip);
if( ddrval != DD_OK )
return FALSE;
ddrval = lpNum->lpVtbl->Restore(lpNum);
if( ddrval != DD_OK )
return FALSE;

// Create and set the palette for the splash bitmap
lpSplashPalette = DDLoadPalette( lpDD, "SPLASH" );
if( NULL == lpSplashPalette )
return CleanupAndExit("DDLoadPalette SPLASH");

// Create and set the palette for the art bitmap
lpArtPalette = DDLoadPalette( lpDD, "DONUTS8" );
if( NULL == lpArtPalette )
return CleanupAndExit("DDLoadPalette DONUTS");

// set the palette before loading the art
lpFrontBuffer->lpVtbl->SetPalette( lpFrontBuffer, lpArtPalette );

hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;

ddrval = DDCopyBitmap( lpDonut, hbm, 0, 0, 320, 384 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

// NOTE: Why are we calling LoadImage again? StretchBlt (which is
// called in DDCopyBitmap) does not work properly when performing
// an 8-bpp to 24- or 32-bpp blt multiple times from the same
// bitmap. The workaround is to call LoadImage before each
// StretchBlt because the first StretchBlt after a LoadImage will
// work.
if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;
}

ddrval = DDCopyBitmap( lpPyramid, hbm, 0, 384, 320, 128 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;
}

ddrval = DDCopyBitmap( lpSphere, hbm, 0, 512, 320, 32 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;
}

ddrval = DDCopyBitmap( lpCube, hbm, 0, 544, 320, 32 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;
}

ddrval = DDCopyBitmap( lpShip, hbm, 0, 576, 320, 256 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

if(ScreenBpp >= 24)
{
DeleteObject( hbm );
hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), "DONUTS8", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );

if( NULL == hbm )
return FALSE;
}

ddrval = DDCopyBitmap( lpNum, hbm, 0, 832, 320, 16 );
if( ddrval != DD_OK )
{
DeleteObject( hbm );
return FALSE;
}

DeleteObject( hbm );

// set colorfill colors and color keys according to bitmap contents
dwFillColor = DDColorMatch( lpDonut, CLR_INVALID );

DDSetColorKey( lpDonut, CLR_INVALID );
DDSetColorKey( lpPyramid, CLR_INVALID );
DDSetColorKey( lpCube, CLR_INVALID );
DDSetColorKey( lpSphere, CLR_INVALID );
DDSetColorKey( lpShip, CLR_INVALID );
DDSetColorKey( lpNum, CLR_INVALID );

return TRUE;
}


int randInt( int low, int high )
{
int range = high - low;
int num = rand() % range;
return( num + low );
}

double randDouble( double low, double high )
{
double range = high - low;
double num = range * (double)rand()/(double)RAND_MAX;
return( num + low );
}


#ifdef USE_DSOUND
void InitializeSound( void )
{
if(!bWantSound)
return; // out of here
bSoundEnabled = FALSE;
if (SUCCEEDED(DirectSoundCreate(NULL, &lpDS, NULL)))
{
if (SUCCEEDED(lpDS->lpVtbl->SetCooperativeLevel(lpDS, hWndMain,
DSSCL_NORMAL)))
{
hsoBeginLevel = SndObjCreate(lpDS, "BeginLevel", 1);
hsoEngineIdle = SndObjCreate(lpDS, "EngineIdle", 1);
hsoEngineRev = SndObjCreate(lpDS, "EngineRev", 1);
hsoSkidToStop = SndObjCreate(lpDS, "SkidToStop", 1);
hsoShieldBuzz = SndObjCreate(lpDS, "ShieldBuzz", 1);
hsoShipExplode = SndObjCreate(lpDS, "ShipExplode", 1);
hsoFireBullet = SndObjCreate(lpDS, "Gunfire", 25);
hsoShipBounce = SndObjCreate(lpDS, "ShipBounce", 4);
hsoDonutExplode = SndObjCreate(lpDS, "DonutExplode", 10);
hsoPyramidExplode = SndObjCreate(lpDS, "PyramidExplode", 12);
hsoCubeExplode = SndObjCreate(lpDS, "CubeExplode", 15);
hsoSphereExplode = SndObjCreate(lpDS, "SphereExplode", 10);
bSoundEnabled = TRUE;

if( bPlayIdle )
SndObjPlay(hsoEngineIdle, DSBPLAY_LOOPING);

if( bPlayBuzz )
SndObjPlay(hsoShieldBuzz, DSBPLAY_LOOPING);

if( bPlayRev )
SndObjPlay(hsoEngineRev, DSBPLAY_LOOPING);
}
else
{
lpDS->lpVtbl->Release(lpDS);
lpDS = NULL;
}
}
}

void DestroySound( void )
{
if(!bWantSound)
return; //No work to be done
bSoundEnabled = FALSE;
if (lpDS)
{
SndObjDestroy(hsoBeginLevel);
hsoBeginLevel = NULL;
SndObjDestroy(hsoEngineIdle);
hsoEngineIdle = NULL;
SndObjDestroy(hsoEngineRev);
hsoEngineRev = NULL;
SndObjDestroy(hsoSkidToStop);
hsoSkidToStop = NULL;
SndObjDestroy(hsoShieldBuzz);
hsoShieldBuzz = NULL;
SndObjDestroy(hsoShipExplode);
hsoShipExplode = NULL;
SndObjDestroy(hsoFireBullet);
hsoFireBullet = NULL;
SndObjDestroy(hsoShipBounce);
hsoShipBounce = NULL;
SndObjDestroy(hsoDonutExplode);
hsoDonutExplode = NULL;
SndObjDestroy(hsoPyramidExplode);
hsoPyramidExplode = NULL;
SndObjDestroy(hsoCubeExplode);
hsoCubeExplode = NULL;
SndObjDestroy(hsoSphereExplode);
hsoSphereExplode = NULL;

lpDS->lpVtbl->Release(lpDS);
lpDS = NULL;
}
}
#endif


int getint(char**p, int def)
{
int i=0;


while (IS_SPACE(**p))
(*p)++;

if (!IS_NUM(**p))
return def;

while (IS_NUM(**p))
i = i*10 + *(*p)++ - '0';

while (IS_SPACE(**p))
(*p)++;

return i;
}