MoofWars/MoofWars.cp

/*
    File:       MoofWars.cp
 
    Contains:   All of the main game functions are defined in here, including initialization,
                tear-down and the actual game loop.
 
    Written by: Timothy Carroll
 
    Copyright:  Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
                4/2/97      Timothy Carroll Now dispose of the color table when we exit
                                            the game.  Technically, we still end up leaking
                                            the appPalette, so this should be corrected if 
                                            InitGame is called more than once.
 
                4/2/97      Timothy Carroll Now check DSpStartup error code.
                
                2/24/97     Timothy Carroll Now conditionally allocate QD globals if we aren't
                            using Metrowerks
                1/23/97     Timothy Carroll allocate QuickDraw globals so that MrC will 
                            compile.
                1/23/97     Timothy Carroll Added include for Moofwars.h so that MrC will 
                            compile
                1/23/97     Timothy Carroll Now include other system headers so that MrC 
                            will compile.
                8/15/96     Timothy Carroll Initial Release
                
 
*/
 
 
 
#if __profile__
#include <profiler.h>
#endif
 
#include <fp.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Sound.h>
 
 
#include "Moofwars.h"
#include "TTileGrid.h"
#include "TSpriteCollection.h"
#include "TShipSprite.h"
#include "TEnemySprite.h"
#include "TShotSprite.h"
 
#if qShowTiming
#include <Timer.h>
#include <FixMath.h>
#include <TextUtils.h>
#endif
 
/********************************************************************************
    GLOBALS
 ********************************************************************************/
 
// Quickdraw Globals
#if TARGET_RT_MAC_CFM
#ifndef  __MWERKS__
QDGlobals   qd;
#endif
#endif
 
CTabHandle              gAppColorTable = NULL;
DSpContextReference     gDrawContext = NULL;
 
// We define a few static variables to hold all the game information.  Only in a few cases
// do we actually need to export this information to other functions.  In general, if a
// particular object needs a piece of information, it should be sent directly to that object,
// rather than be imported as a global.
 
// The playing field
static TTileGrid        *theGrid = NULL;
WorldRect32             gWorldBounds;
 
// All of the sprite groups
static TSpriteCollection        *gFriendlyShipGroup = NULL;
static TSpriteCollection        *gEnemyShipGroup = NULL;
static TSpriteCollection        *gFriendlyShotGroup = NULL;
static TSpriteCollection        *gEnemyShotGroup = NULL;
 
// We keep track of the two ships separately as well,
// as we can use them as the camera location
static TShipSprite      *gFriendlyShip = NULL;
static TEnemySprite     *gEnemyShip = NULL;
 
// Although the sprites would load their own graphics (because of the magic of our TGraphic
// collection, we can preload the graphics and pass them to the TSprite constructors for
// speed.  These variables will manage the loading and unloading of the sprites.
 
TGraphicCollection *gShipGraphics = NULL;
TGraphicCollection *gEnemyGraphics = NULL;
TGraphicCollection *gShotGraphics = NULL;
 
#if qUsingSound
// Sounds
SndReference    gSounds[10];
Boolean         gPlayingSound = true; // If false, no sounds will play
#endif
 
#if qShowTiming
// Miscellaneous
long            gShotsOnBoard = 0;  // Holds the total number of shots in game
#endif
 
// Holds the current state of the keyboard and other input devices when processing
// the list of sprites.
TInputState         gCommandKeys;
TCommandTimer       theTimer;
 
// A standard set of lookup tables in 16.16 fixed notation, so that we can quickly
// do sin and cos.  We currently use 48 frames because thats easy to do from
// Specular Infini-D.  360/48 = 7.5¡ per angle.
SInt32          gSinLookup[48];
SInt32          gCosLookup[48];
 
 
/********************************************************************************
    GLOBALS
 ********************************************************************************/
 
void main (void);
 
// Initialize everything for the game
static OSStatus InitGame (void);
 
// Fills in the initial values in the lookup tables.
static OSStatus InitAngleLookups (void);
 
#if qUsingSound
// Initializes Sound API and loads all the sound resources
static OSStatus LoadSounds (void);
#endif
 
// This creates a draw sprocket context
static OSStatus InitDrawSprocket (void);
 
// This loads all of our graphics and tile resources
static OSStatus LoadGameGraphics (void);
 
// This creates the grid, the initial sprites and sprite groups.
static OSStatus CreateGame (void);
 
// The actual event loop
static OSStatus RunGame (void);
 
// The cleanup code for InitGame
static OSStatus CloseGame (void);
 
 
/********************************************************************************
    main
    
    All main does is the standard mac init, followed by initializing the game
    and running it once.  In a real game it would probably put up a splash screen
    with some widgets on it.
 ********************************************************************************/
void main (void)
{
 
    int         loop;
    OSStatus    theErr = noErr;
 
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();
 
    MaxApplZone();
    
    for (loop = 0; loop < 30; loop++)
        MoreMasters();
        
    theErr = InitGame();
    FAIL_OSERR (theErr, "\pCouldn't initialize new game")
    theErr = RunGame();
    FAIL_OSERR (theErr, "\pFailure in game running code")
    
    error:
    cleanup:
        theErr= CloseGame();
}
 
 
/************************************************************************************
 InitGame
 
 Init Game loads all the sprites and sets up all the variables for running a game.
 Eventually, we can split out a large number of these routines so that they only
 need to be called when the application actually initializes, rather than once per game.
 
 Of course, our loading routines are way cool, so we may not care. :)
 
 
 ************************************************************************************/
 
OSStatus InitGame (void)
{
    OSStatus                theErr = noErr;
    PaletteHandle           appPalette;
 
    // Load our initial color table and make it the default palette for the entire application.
    // (We'll also send this color table to DrawSprocket)
    
    gAppColorTable = GetCTable (kAppColorTableResID);
    FAIL_NIL (gAppColorTable, "\pFailed to load the color table")
    appPalette = NewPalette (256, gAppColorTable, pmExplicit + pmTolerant, 0);
    FAIL_NIL (appPalette, "\pFailed to convert the color table into a palette")
    SetPalette ( (WindowRef) -1L, appPalette, false);   
 
    // Initialize the sound effects
#if qUsingSound
    theErr = LoadSounds();
    FAIL_OSERR (theErr, "\pFailed to load the sounds")
#endif
    
// Initialize our predefined arrays of the move angles.
    theErr = InitAngleLookups();
    FAIL_OSERR (theErr, "\pFailed to initialize the lookup tables")
 
// Initialize the keyboard command routines.
 
#if qSyncTiming
    theTimer.Initialize(30); // 30 frames per second
#else
    theTimer.Initialize (0); // one command per frame, fast as possible.
#endif
 
// Create a draw sprocket context
    theErr = InitDrawSprocket ();
    FAIL_OSERR (theErr, "\pFailed to initialize draw sprocket")
 
// Load the game graphics
    theErr = LoadGameGraphics();
    FAIL_OSERR (theErr, "\pFailed to preload the game graphics")
 
// Create all of the game objects -- note that the stuff in here is probably the only thing that
// would be run more than once every time the application runs -- the rest of the initialization code
// will all run once.
    theErr = CreateGame();
    FAIL_OSERR (theErr, "\pFailed to create the game objects")
    
    return noErr;
    
    error:
    
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;  
}
 
/************************************************************************************
InitAngleLookups 
 
 ************************************************************************************/
 
OSStatus InitAngleLookups(void)
{
    int loop;
    
    for (loop = 0; loop < 48; loop++)
    {
        double angle = (90 - loop * 7.5)/57.296;
        
        gSinLookup[loop] = (1<<14) * sin (angle);
        gCosLookup[loop] = (1<<14) * -cos (angle);
    }
 
    return noErr;
}
 
 
/************************************************************************************
 LoadSounds
 
This initializes whatever sound API we're using and loads all of the sound resources.
 
 ************************************************************************************/
 
#if qUsingSound
OSStatus LoadSounds (void)
{
    OSStatus theErr;
    // Initialize and load all of the game sounds.
 
    theErr = HY_Setup (kNormal, 5);
    FAIL_OSERR (theErr, "\p Failed to set up hollywood API")
    gSounds[0] = HY_GetSoundResource(6000);
    FAIL_NIL(gSounds[0], "\p Failed to allocate the sound resource")
    gSounds[1] = HY_GetSoundResource(6001);
    FAIL_NIL(gSounds[1], "\p Failed to allocate the sound resource")
    gSounds[2] = HY_GetSoundResource(6002);
    FAIL_NIL(gSounds[2], "\p Failed to allocate the sound resource")
    gSounds[3] = HY_GetSoundResource(6003);
    FAIL_NIL(gSounds[3], "\p Failed to allocate the sound resource")
    gSounds[4] = HY_GetSoundResource(6004);
    FAIL_NIL(gSounds[4], "\p Failed to allocate the sound resource")
    gSounds[5] = HY_GetSoundResource(6005);
    FAIL_NIL(gSounds[5], "\p Failed to allocate the sound resource")
    
    return noErr;
    
    error:
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;  
}
#endif
 
 
/************************************************************************************
InitDrawSprocket 
 
 ************************************************************************************/
OSStatus InitDrawSprocket (void)
{
    OSStatus                theErr = noErr;
    DSpContextAttributes    screenData;
    RGBColor                fade;
    
// Create a Draw Sprocket context for the screen.
    theErr = DSpStartup();
    FAIL_OSERR (theErr, "\pError: Failed to start up DrawSprocket")
    
    // fill in our requirements.  Anything we place here we need to have for our game to work properly.
    screenData.frequency                = 0;
    screenData.displayWidth             = 640;
    screenData.displayHeight            = 480;
    screenData.reserved1                = 0;
    screenData.reserved2                = 0;
    screenData.colorNeeds               = kDSpColorNeeds_Require;
    screenData.colorTable               = gAppColorTable;
    screenData.contextOptions           = 0;
    screenData.backBufferDepthMask      = kDSpDepthMask_8;
    screenData.displayDepthMask         = kDSpDepthMask_8;
    screenData.backBufferBestDepth      = 8;
    screenData.displayBestDepth         = 8;
    screenData.pageCount                = 2;
    screenData.gameMustConfirmSwitch    = false;
    screenData.reserved3[0]             = 0;
    screenData.reserved3[1]             = 0;
    screenData.reserved3[2]             = 0;
    screenData.reserved3[3]             = 0;
    
    theErr = DSpUserSelectContext(&screenData, 0,NULL, &gDrawContext);
    FAIL_OSERR (theErr, "\p Failed to retrieve a drawing context")
    
    // We should now alter the screenData to reflect things that would be useful to have, but aren't
    // requirements.
    
#if qUsePageFlip
    screenData.contextOptions   |= kDSpContextOption_PageFlip;
    screenData.pageCount        = 3;
#endif
 
#if (!qSyncToDrawVBL)
    screenData.contextOptions   |= kDSpContextOption_DontSyncVBL;
#endif
    
    // Reserve the screen and set it to active.
    theErr = DSpContext_Reserve (gDrawContext, &screenData);
    FAIL_OSERR (theErr, "\pFailed to reserve the context")
#if 0
    theErr = DSpContext_SetMaxFrameRate (gDrawContext, 60);
    FAIL_OSERR (theErr, "\p Failed to set the maximum frame rate")
#endif  
 
    fade = kBlack; // DrawSprocket requires this to be a non-const param
 
    theErr = DSpContext_FadeGammaOut (gDrawContext, &fade);
    FAIL_OSERR (theErr, "\p Failed to fade out")
 
    theErr = DSpContext_SetState (gDrawContext, kDSpContextState_Active);
    FAIL_OSERR (theErr, "\pFailed to activate the draw context")
 
    theErr = DSpContext_FadeGammaIn (gDrawContext, &fade);
    FAIL_OSERR (theErr, "\p Failed to fade in")
 
    return noErr;
    
    error:
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
}
 
 
/************************************************************************************
LoadGameGraphics 
 ************************************************************************************/
 
OSStatus LoadGameGraphics (void)
{
    OSStatus theErr = noErr;
// Preload and lock all of our game's graphics so that everything runs smoothly.
    
    gShipGraphics = TGraphicCollection::NewCollection (kShipResourceID);
    FAIL_NIL (gShipGraphics, "\pcouldn't load friendly ship graphics")
    gShipGraphics->LockCollection();
    
    gShotGraphics = TGraphicCollection::NewCollection (kShotResourceID);
    FAIL_NIL (gShotGraphics, "\pcouldn't load friendly shot graphics")
    gShotGraphics->LockCollection();
 
// This version uses the same graphics for the enemy and friendly units, but you can
// insert your own favorite graphics instead.   
    #if 0
    gEnemyGraphics = TGraphicCollection::NewCollection (kEnemyResourceID);
    FAIL_NIL (gEnemyGraphics, "\pcouldn't load enemy graphics")
    gEnemyGraphics->LockCollection();
#endif
 
    return noErr;
    
    error:
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
}
 
 
/************************************************************************************
CreateGame 
 
 ************************************************************************************/
OSStatus CreateGame (void)
{
    OSStatus            theErr = noErr;
    Rect                boundsRect  = {0,0, 480, 640};
    
    TShipSpriteData     shipData;
    TEnemySpriteData    enemyData;
 
// Load the Grid and Tiles.  Note that we do load the tile graphics here as well --
// we could move that to LoadGameGraphics if we wanted.
// Load the tile grid information
    theGrid = new TTileGrid();
    theErr = theGrid->CreateGridFromResource(kGridResourceID);
    FAIL_OSERR (theErr, "\p Failed to load the grid resource")
    
    // Initialize the world boundaries in 16.16 fixed point coordinates.
    
    gWorldBounds.left = 0;
    gWorldBounds.top = 0;
    gWorldBounds.right = theGrid->GetGridWidth() * (32 << 16);
    gWorldBounds.bottom = theGrid->GetGridHeight() * (32 << 16);
    
    
//  Create groups for all of our game's sprites
 
    gFriendlyShipGroup = new TSpriteCollection ();
    FAIL_NIL (gFriendlyShipGroup, "\pcouldn't create friendly ship group")
    gEnemyShipGroup = new TSpriteCollection ();
    FAIL_NIL (gEnemyShipGroup, "\pcouldn't create enemy ship group")
    gFriendlyShotGroup = new TSpriteCollection ();
    FAIL_NIL (gFriendlyShotGroup, "\pcouldn't create friendly shot group")
    gEnemyShotGroup = new TSpriteCollection ();
    FAIL_NIL (gEnemyShotGroup, "\pcouldn't create enemy shot group")    
    
 
// Create the good guy ship
    
    shipData.spriteData.spriteType = TShipSprite::kSpriteType;
    shipData.spriteData.initialX = (100) << 16;
    shipData.spriteData.initialY = (100) << 16;
    shipData.spriteData.initialXVelocity = 0;
    shipData.spriteData.initialYVelocity = 0;
    shipData.spriteData.visibility = kVisible;
    shipData.spriteData.face = 0;
    shipData.spriteData.collectionID = 1000;
    shipData.spriteData.preloadedCollection = gShipGraphics;
    shipData.rotateInterval = 1;
    shipData.shotInterval = 1;
    shipData.shotsGroup = gFriendlyShotGroup;
    
    gFriendlyShip = new TShipSprite(&shipData);
    gFriendlyShip->AddToGroup (gFriendlyShipGroup);
    
    // Create the enemy ship
    // We are using the same graphics here, but this can be changed if you want
    // a separate villain.
    enemyData.spriteData.spriteType = TEnemySprite::kSpriteType;
    enemyData.spriteData.initialX = (1200) << 16;
    enemyData.spriteData.initialY = (1200) << 16;
    enemyData.spriteData.initialXVelocity = 0;
    enemyData.spriteData.initialYVelocity = 0;
    enemyData.spriteData.visibility = kVisible;
    enemyData.spriteData.face = 0;
    enemyData.spriteData.collectionID = 1000;
    enemyData.spriteData.preloadedCollection = gShipGraphics;
    enemyData.rotateInterval = 1;
    enemyData.shotInterval = 1;
    enemyData.shotsGroup = gEnemyShotGroup;
        
    gEnemyShip = new TEnemySprite(&enemyData);
    gEnemyShip->AddToGroup (gEnemyShipGroup);
    
    return noErr;
    
    error:
    if (theErr == noErr)
        theErr = paramErr;
    return theErr;
}
 
 
OSStatus RunGame (void)
{
    // Is game currently running?
    Boolean         running = true;
    
    // We need a separate boolean to determine is the sound key is still being held down -- if it is, we
    // don't toggle.  Otherwise we'd keep turning the sound off and on.
    Boolean         soundKeyDown = false;
    
    OSStatus        theErr = noErr;
 
    //DrawSprocket will return us a CGrafPtr to draw into.
    CGrafPtr        buffer;
    
    //The center of the tile grid is tracked by a camera position.  In this version of the code, this
    //position is identical to the ship, but it could actually be any arbitrary point.  Missile-CAM!
    SInt32          cameraX, cameraY;
    SInt32          tileTop, tileLeft;
    Rect            clipping = {0,0,480,640};
    
#if qShowTiming
    UnsignedWide    startMicroSec, endMicroSec;
    Str255          theString;
    long            frame;
#endif
    
    // We play a beep to signal the start of the game.
    SysBeep(2);
    HideCursor();
    // Basic structure of the game loop is:
    //  * erase all sprites
    //  * draw all sprites in their new locations
    //  * refresh the screen
    //  * move and hit test the sprites based on any queued commands.
    
    // Erasing and drawing is relative to a clipping rectangle and a graphics scale.
    // If at all possible, keep different clipping areas in the game from overlapping.
    // failing that, do all erases in all clip areas before any sprites are drawn.
    
    // For the current version (fixed map), all scaling data is constant, so we can 
    // set it once and ignore it.  If we are changing the scaling data, then we need to
    // move this section into the loop.  Note that scaling data will be different
    // for erases and draws in that case.
 
    // Set the initial camera location.
    gFriendlyShip->GetCurrentWorldLocation(&cameraX, &cameraY);
 
#if __profile__
    ProfilerInit(collectDetailed, PPCTimeBase, 200, 30);
#endif
 
#if qShowTiming
    Microseconds(&startMicroSec);
#endif
 
#if qSyncTiming 
    theTimer.AcceptCommands(true);
#endif
 
    while (running)
    {
        /*************   Point at the Current Buffer    ***************************/
        theErr =  DSpContext_GetBackBuffer( gDrawContext,kDSpBufferKind_Normal, &buffer );
    #if qDebugging
        FAIL_OSERR(theErr, "\p Failed to retrieve a drawing surface.")
    #endif      
 
        SetPort ((GrafPtr) buffer);
        SetDestinationBuffer (buffer->portPixMap, NULL);
        
        /*************   Set the camera location        ***************************/
        
        gFriendlyShip->GetCurrentWorldLocation(&cameraX, &cameraY);
        
        SetBufferClip (&clipping);
        SetWorldOrigin(cameraX, cameraY);
 
 
        /*************   Draw the Tile Grid   ************************/
        tileLeft = (cameraX >> 16) - 320;
        tileTop = (cameraY >> 16) - 240;
        
        theGrid->DrawGrid (&clipping, tileTop,tileLeft);
 
        /**************  Show the time to draw the current frame *******/
    #if qShowTiming
        Microseconds(&endMicroSec);
        WideSubtract((wide *) &endMicroSec, (wide *) &startMicroSec);
        Microseconds(&startMicroSec); // start timing the next frame
        frame = 1000000.0/endMicroSec.lo;
        
        RGBForeColor (&kWhite);
        NumToString (frame, theString);
        MoveTo (10,470);
        DrawString (theString);
        NumToString (gShotsOnBoard, theString);
        MoveTo (10, 450);
        DrawString (theString);
        RGBForeColor (&kBlack);
    #endif  
 
        /*************   Draw all the sprites ***************/
        
        gEnemyShipGroup->DrawSpriteGroup ();
        gFriendlyShipGroup->DrawSpriteGroup ();
        gEnemyShotGroup->DrawSpriteGroup ();
        gFriendlyShotGroup->DrawSpriteGroup ();
 
            
        /*************   Refresh the screen ***************/
            
        theErr =  DSpContext_SwapBuffers( gDrawContext, NULL, NULL);
    #if qDebugging
        FAIL_OSERR (theErr,"\p Failed to swap the buffers.")
    #endif      
 
        
        /*************   Process Commands    *************************************/
 
        // If we are syncing our timing, we'll make sure we have at least one command to process,
        // and then loop until all of the commands are completed.  Otherwise we just grab one key
        // state and call through the loop once.
 
    #if qSyncTiming
        // Wait for a command to actually show up
        while (!theTimer.IsCommandWaiting())
            ;
 
        // Process all waiting commands.
        while (theTimer.RetrieveCommand (&gCommandKeys))
    #else
        theTimer.RetrieveCommand (&gCommandKeys);
    #endif
        {       
            gFriendlyShipGroup->ProcessSpriteGroup();
            gEnemyShipGroup->ProcessSpriteGroup();
            gFriendlyShotGroup->ProcessSpriteGroup();
            gEnemyShotGroup->ProcessSpriteGroup();
            
            gFriendlyShotGroup->HitTest (gEnemyShipGroup);
            gEnemyShotGroup->HitTest (gFriendlyShipGroup);
    
            // Check for exit game
            if (CheckKey ((unsigned char *) &gCommandKeys, kExitGame))
                running = false;
                
            // Check for debugger break
            if (CheckKey ((unsigned char *) &gCommandKeys, kDebugger))
            {
                FlushEvents (everyEvent,0);
                Debugger();
            }
    
    #if qUsingSound     
            // Give the sound API some time to service tasks.
            HY_ServiceTasks();  
            
            // play some sounds.
            if (CheckKey ((unsigned char *) &gCommandKeys, kOne) && gPlayingSound)
            {
                HY_PlaySoundHandle (1, gSounds[0], NULL, 0, false);
            }
            if (CheckKey ((unsigned char *) &gCommandKeys, kTwo)  && gPlayingSound)
            {
                HY_PlaySoundHandle (1, gSounds[1], NULL, 0, false);
            }
            if (CheckKey ((unsigned char *) &gCommandKeys, kThree)  && gPlayingSound)
            {
                HY_PlaySoundHandle (1, gSounds[2], NULL, 0, false);
            }
            if (CheckKey ((unsigned char *) &gCommandKeys, kFour)  && gPlayingSound)
            {
                HY_PlaySoundHandle (1, gSounds[3], NULL, 0, false);
            }
                
            // toggle the sound setting
            if (CheckKey ((unsigned char *) &gCommandKeys,kSoundToggle))
            {
                if (!soundKeyDown)
                {
                    soundKeyDown = true;
                    gPlayingSound = !gPlayingSound;
                    if (!gPlayingSound)
                    {
                    HY_StopSample(1);
                    HY_StopSample(2);
                    HY_StopSample(3);
                    HY_StopSample(4);
                    }
                }
            }
            else
            {
                soundKeyDown = false;
            }
        #endif      
        }
    }
    
    
#if __profile__
    ProfilerSetStatus(false);
    ProfilerDump("\pMoofWarsDump");
    ProfilerTerm();
#endif
    
    ShowCursor();
    // We want to flush all the keyboard events we haven't been taking.
    theTimer.AcceptCommands (false);
    FlushEvents(everyEvent, 0);
    
    return noErr;
#if qDebugging
    error:
    return theErr;
#endif
}
 
OSStatus CloseGame (void)
{
    // This doesn't clean up everything just yet, but it should!
 
    if (gFriendlyShipGroup != NULL)
    {
        delete gFriendlyShipGroup;
        gFriendlyShipGroup = NULL;  
    }
    
    if (gEnemyShipGroup != NULL)
    {
        delete gEnemyShipGroup;
        gEnemyShipGroup = NULL;
    }
        
    if (gFriendlyShotGroup != NULL)
    {
        delete gFriendlyShotGroup;
        gFriendlyShotGroup = NULL;
    }
        
    if (gEnemyShotGroup != NULL)
    {
        delete gEnemyShotGroup;
        gEnemyShotGroup = NULL;
    }
 
    if (theGrid != NULL)
    {
        delete theGrid;
        theGrid = NULL;
    }
 
    if (gShipGraphics != NULL)
    {
        gShipGraphics->DisposeReference;
        gShipGraphics = NULL;
    }
        
    if (gShotGraphics != NULL)
    {
        gShotGraphics->DisposeReference;
        gShotGraphics = NULL;
    }
    
    if (gDrawContext != NULL)
    {
        DSpContext_SetState (gDrawContext, kDSpContextState_Inactive);
        DSpContext_Release( gDrawContext);
        DSpShutdown();
    }
 
    if (gAppColorTable)
        DisposeCTable (gAppColorTable);
 
    return noErr;
}
 
 
Boolean CheckKey (unsigned char *PtrToKeyMap, short theKey)
{
    unsigned char   theByte, theBit;
    short           byteIndex;
    
    byteIndex = theKey >> 3;
    theByte = *(unsigned char *)(PtrToKeyMap + byteIndex);
    theBit = 1L<<(theKey & 7);
    
    return (theByte & theBit);
}