G4Graphics.c

/*
    File:       G4Graphics.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
 
    File Ownership:
 
        DRI:                xxx put dri here xxx
 
        Other Contact:      xxx put other contact here xxx
 
        Technology:         xxx put technology here xxx
 
    Writers:
 
        (sjb)   Steve Bollinger
 
    Change History (most recent first):
 
         <3>      7/1/98    sjb     Update to CWPro 2
*/
 
 
//============================================================================
//----------------------------------------------------------------------------
//                                  Graphics.c
//----------------------------------------------------------------------------
//============================================================================
 
// I like to isolate all the graphic routines - put them in their own file.
// This way all the thousands of Rect variables and Pixmaps have a place to go.
// Anyway, this file contains all the drawing routines.
 
#include "G4Externs.h"
#include <Palettes.h>
#include <QuickdrawText.h>
#include <Fonts.h>
#include <TextUtils.h>
#include <Events.h>
 
#define kUpperEyeHeight         100
#define kLowerEyeHeight         200
#define kNumLightningPts        8
 
 
void QuickUnionRect (Rect *, Rect *, Rect *);
void CheckPlayerWrapAround (void);
void DrawHand (void);
void DrawEye (void);
void DrawPlayer (void);
void CheckEnemyWrapAround (short);
void DrawEnemies (void);
void DrawBanner (void);
void DrawLava(void);
 
void HandleLava(void);
void HandlePixelShatter(void);
 
Rect        backSrcRect, workSrcRect, obSrcRect, playerSrcRect;
Rect        numberSrcRect, idleSrcRect, enemyWalkSrcRect, enemyFlySrcRect;
Rect        obeliskRects[4], playerRects[11], numbersSrc[11], numbersDest[11];
Rect        flameSrcRect, flameDestRects[2], flameRects[4], eggSrcRect;
Rect        platformSrcRect, platformCopyRects[9], helpSrcRect, eyeSrcRect, bannerSrcRect;
Rect        helpSrc, helpDest, handSrcRect, handRects[2], eyeRects[4];
Point       leftLightningPts[kNumLightningPts], rightLightningPts[kNumLightningPts];
CGrafPtr    origBackSrcMap, backSrcMap, workSrcMap, obeliskSrcMap, playerSrcMap, eyeSrcMap;
CGrafPtr    numberSrcMap, idleSrcMap, enemyWalkSrcMap, enemyFlySrcMap;
CGrafPtr    flameSrcMap, eggSrcMap, bannerSrcMap, platformSrcMap, helpSrcMap, handSrcMap;
GrafPtr     playerMaskMap, enemyWalkMaskMap, enemyFlyMaskMap, eggMaskMap;
GrafPtr     handMaskMap, eyeMaskMap, obeliskMaskMap;
Boolean     whichList, helpOpen, scoresOpen;
 
short numPixelShatter;
pixelShatter thePixelShatter[kMaxPixelShatter];
 
extern  handInfo    theHand;
extern  eyeInfo     theEye;
extern  prefsInfo   thePrefs;
extern  playerType  thePlayer;
extern  enemyType   theEnemies[];
extern  Rect        enemyRects[24];
extern  long        theScore, wasTensOfThousands;
extern  short       livesLeft, levelOn, numEnemies;
extern  Boolean     evenFrame;
 
extern short lightningCount;
void HandleLightning();
 
 
extern short oldFrameRate;
 
// in DR3 of DrawSprocket the underlay buffer will give the game the
// ability to not worry about keeping track fo the update rects.  As it
// is right now, if you use update rects you have to save the update
// rectangle for each frame, and the number of frames will vary depending
// on the state of triple buffering and page flipping.
// 
// Once the Underlay buffers are operational, enabiling this #define
// will allow DS to optimize the screen updates instead of doing
// a full screen copy (as happens in Glypha)
//#define USE_INVALID_RECTS 1
 
//==============================================================  Functions
//--------------------------------------------------------------  DrawPlatforms
 
// This function draws all the platforms on the background pixmap and theÉ
// work pixmap.  It needs to know merely how many of them to draw.
 
void DrawPlatforms (short howMany)
{
    if (howMany > 3)            // If there are more than 3 platformsÉ
    {                           // Draw a platform to background pixmap.
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[2], &platformCopyRects[7], srcCopy, 0L);
        
                                // Draw a platform to work pixmap.
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[7], &platformCopyRects[7], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[7] );
        #endif
 
                                // Add rectangle to update list to be drawn to screen.
                                // Ditto for a second platform.
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[4], &platformCopyRects[8], srcCopy, 0L);
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[8], &platformCopyRects[8], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[8] );
        #endif
 
    }
    else                        // If there are 3 or less platformsÉ
    {
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[3], &platformCopyRects[7], srcCopy, 0L);
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[7], &platformCopyRects[7], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[7] );
        #endif
 
        
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[5], &platformCopyRects[8], srcCopy, 0L);
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[8], &platformCopyRects[8], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[8] );
        #endif
 
    }
    
    if (howMany > 5)        // If there are more than 5 platformsÉ
    {
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[0], &platformCopyRects[6], srcCopy, 0L);
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[6], &platformCopyRects[6], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[6] );
        #endif
 
    }
    else                    // If there are 5 or less platformsÉ
    {
        CopyBits(&((GrafPtr)platformSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &platformCopyRects[1], &platformCopyRects[6], srcCopy, 0L);
        CopyBits(&((GrafPtr)backSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &platformCopyRects[6], &platformCopyRects[6], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &platformCopyRects[6] );
        #endif
 
    }
}
 
//--------------------------------------------------------------  ScrollHelp
 
// This function scrolls the help screen.  You pass it a number of pixelsÉ
// to scroll up or down (positive or negative number).
 
void ScrollHelp (short scrollDown)
{
    // don't do any drawing, drawing is done by the idle animation automatically
    
    OffsetRect(&helpSrc, 0, scrollDown);        // Move the source rectangle.
    
    if (helpSrc.bottom > 398)                   // Check to see we don't go too far.
    {
        helpSrc.bottom = 398;
        helpSrc.top = helpSrc.bottom - 199;
    }
    else if (helpSrc.top < 0)
    {
        helpSrc.top = 0;
        helpSrc.bottom = helpSrc.top + 199;
    }
}
 
//--------------------------------------------------------------  OpenHelp
 
// Bring up the help screen.  This is a kind of "wipe" or "barn door" effect.
 
void OpenHelp (void)
{
    Rect        wallSrc, wallDest;
    short       i;
    
    SetRect(&helpSrc, 0, 0, 231, 0);    // Initialize source and destination rects.
    helpDest = helpSrc;
    OffsetRect(&helpDest, 204, 171);
    
    SetRect(&wallSrc, 0, 0, 231, 199);
    OffsetRect(&wallSrc, 204, 171);
    wallDest = wallSrc;
    
    for (i = 0; i < 199; i ++)          // Loop through 1 pixel at a time.
    {
        DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
 
        DumpBackToWorkMap();                    // clear the screen
        HandleLava();
        HandlePixelShatter();
        DrawTorches();
        DrawBanner();
        DrawLava();
        DrawPixelShatter();
 
        LogNextTick(1L);                // Speed governor.
        helpSrc.bottom++;               // Grow help source rect.
        helpDest.bottom++;              // Grow help dest as well.
        wallSrc.bottom--;               // Shrink wall source.
        wallDest.top++;                 // Shrink wall dest.
        
                                        // So, as the help graphic grows, the wall graphicÉ
                                        // shrinks.  Thus it is as though the wall isÉ
                                        // lifting up to expose the help screen beneath.
        
        CopyBits(   &((GrafPtr)helpSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &helpSrc, &helpDest, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &helpDest );
        #endif
 
 
        CopyBits(   &((GrafPtr)backSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &wallSrc, &wallDest, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &wallDest );
        #endif
 
                                        // Copy slightly larger help screen.
 
        DSpContext_SwapBuffers( gTheContext, NULL, NULL );
    }
    helpOpen = TRUE;                    // When done, set flag to indicate help is open.
}
 
//--------------------------------------------------------------  CloseWall
 
// Close the wall over whatever screen is up (help screen or high scores).
// Since the wall just comes down over the opening - covering whatever was beneath,É
// it's simpler than the above function.
 
void CloseWall (void)
{
    DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
    DumpBackToWorkMap();                    // clear the screen
    DSpContext_SwapBuffers( gTheContext, NULL, NULL );
}
 
//--------------------------------------------------------------  OpenHighScores
 
// This function is practically identical to the OpenHelp().  The only realÉ
// difference is that we must first draw all the high scores offscreen beforeÉ
// lifting the wall to reveal them.
 
void OpenHighScores (void)
{
    RGBColor    theRGBColor, wasColor;
    Rect        wallSrc, wallDest;
    Rect        scoreSrc, scoreDest;
    Str255      scoreStr;
    short       i, scoreWide;
    
    SetRect(&scoreSrc, 0, 0, 231, 0);               // Initialize source and dest rects.
    OffsetRect(&scoreSrc, 204, 171);
    scoreDest = scoreSrc;
    
    SetRect(&wallSrc, 0, 0, 231, 199);
    OffsetRect(&wallSrc, 204, 171);
    wallDest = wallSrc;
    
    SetPort((GrafPtr)workSrcMap);                   // We'll draw scores to the work pixmap.
    PaintRect(&wallSrc);                            // Paint it black.
    
    GetForeColor(&wasColor);                        // Save the foreground color.
    
    TextFont(kFontIDGeneva);                        // Use Geneva 12 point Bold font.
    TextSize(12);
    TextFace(bold);
    
    Index2Color(132, &theRGBColor);                 // Get the 132nd color in RGB form.
    RGBForeColor(&theRGBColor);                     // Make this color the pen color.
    MoveTo(scoreSrc.left + 36, scoreSrc.top + 20);  // Get pen in right position to draw.
    DrawString("\pGlypha IV High Scores");          // Draw the title.
    
    TextFont(kFontIDGeneva);                        // Use Geneva 9 point Bold font.
    TextSize(9);
    TextFace(bold);
    
    for (i = 0; i < 10; i++)                        // Walk through all 10 high scores.
    {
        Index2Color(133, &theRGBColor);             // Use color 133 (in palette).
        RGBForeColor(&theRGBColor);
        NumToString((long)i + 1L, scoreStr);        // Draw "place" (1, 2, 3, É).
        MoveTo(scoreSrc.left + 8, scoreSrc.top + 40 + (i * 16));
        DrawString(scoreStr);
        
        Index2Color(128, &theRGBColor);             // Use color 128 (from palette).
        RGBForeColor(&theRGBColor);
        MoveTo(scoreSrc.left + 32, scoreSrc.top + 40 + (i * 16));
        DrawString(thePrefs.highNames[i]);          // Draw the high score name (Sue, É).
        
        Index2Color(164, &theRGBColor);             // Use color 164 (from palette).
        RGBForeColor(&theRGBColor);
        NumToString(thePrefs.highScores[i], scoreStr);
        scoreWide = StringWidth(scoreStr);          // Right justify.
        MoveTo(scoreSrc.left + 191 - scoreWide, scoreSrc.top + 40 + (i * 16));
        DrawString(scoreStr);                       // Draw the high score (12,000, É).
        
        Index2Color(134, &theRGBColor);             // Use color 134 (from palette).
        RGBForeColor(&theRGBColor);
        NumToString(thePrefs.highLevel[i], scoreStr);
        scoreWide = StringWidth(scoreStr);          // Right justify.
        MoveTo(scoreSrc.left + 223 - scoreWide, scoreSrc.top + 40 + (i * 16));
        DrawString(scoreStr);                       // Draw highest level (12, 10, É).
    }
    
    RGBForeColor(&wasColor);                        // Restore foreground color.
 
    for (i = 0; i < 199; i ++)                      // Now the standard scroll functions.
    {
        WaitForNextTick();
        
        DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
        DumpBackToWorkMap();                    // clear the screen
    
        LogNextTick(1L);                // Speed governor.
        scoreSrc.bottom++;
        scoreDest.bottom++;
        wallSrc.bottom--;
        wallDest.top++;
        
                                        // So, as the help graphic grows, the wall graphicÉ
                                        // shrinks.  Thus it is as though the wall isÉ
                                        // lifting up to expose the help screen beneath.
        
        CopyBits(   &((GrafPtr)workSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &scoreSrc, &scoreDest, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &scoreDest );
        #endif
 
 
        CopyBits(   &((GrafPtr)backSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &wallSrc, &wallDest, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &wallDest );
        #endif
 
                                        // Copy slightly larger help screen.
 
        DSpContext_SwapBuffers( gTheContext, NULL, NULL );
    }
    
    scoresOpen = TRUE;                              // Flag that the scores are up.
}
 
//--------------------------------------------------------------  UpdateLivesNumbers
 
// During a game, this function is called to reflect the current number of lives.
// This is "lives remaining", so 1 is subtracted before displaying it to the screen.
// The lives is "wrapped around" after 99.  So 112 lives will display as 12.  It'sÉ
// a lot easier to handle numbers this way (it beats a recursive function that mightÉ
// potentially draw across the entire screen.
 
void UpdateLivesNumbers (void)
{
    short       digit;
    
    digit = (livesLeft - 1) / 10;       // Get the "10's" digit.
    digit = digit % 10L;                // Keep it less than 10 (0 -> 9).
    if ((digit == 0) && ((livesLeft - 1) < 10))
        digit = 10;                     // Use a "blank" space if zero and less than 10.
                                        // Draw digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[0], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[0] );
    #endif
 
 
    digit = (livesLeft - 1) % 10;       // Get 1's digit.
                                        // Draw digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[1], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[1] );
    #endif
 
}
 
//--------------------------------------------------------------  UpdateScoreNumbers
 
// This function works just like the above function.  However, we allow theÉ
// score to go to 6 digits (999,999) before rolling over.  Note however, thatÉ
// in both the case of the score, number of lives, etc., the game does in factÉ
// keep track of the "actual" number.  It is just that only so many digits areÉ
// being displayed.
 
void UpdateScoreNumbers (void)
{
    long        digit;
 
    digit = theScore / 100000L;     // Get "hundreds of thousands" digit
    digit = digit % 10L;            // Clip off anything greater than 9.
    if ((digit == 0) && (theScore < 1000000L))
        digit = 10;                 // Use blank space if zero.
                                    // Draw digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[2], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[2] );
    #endif
 
 
    digit = theScore / 10000L;      // Get "tens of thousands" digit.
    if (digit > wasTensOfThousands) // Check for "extra life" here.
    {
        livesLeft++;                // Increment number of lives.
        UpdateLivesNumbers();       // Reflect new lives on screen.
        wasTensOfThousands = digit; // Note that life was given.
    }
    digit = digit % 10L;            // Clip off anything greater than 9.
    if ((digit == 0) && (theScore < 100000L))
        digit = 10;                 // Use blank space if zero.
                                    // Draw digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[3], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[3] );
    #endif
 
    
    digit = theScore / 1000L;       // Handle "thousands" digit.
    digit = digit % 10L;
    if ((digit == 0) && (theScore < 10000L))
        digit = 10;
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[4], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[4] );
    #endif
 
 
    digit = theScore / 100L;        // Handle 100's digit.
    digit = digit % 10L;
    if ((digit == 0) && (theScore < 1000L))
        digit = 10;
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[5], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[5] );
    #endif
 
 
    digit = theScore / 10L;         // Handle 10's digit.
    digit = digit % 10L;
    if ((digit == 0) && (theScore < 100L))
        digit = 10;
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[6], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[6] );
    #endif
 
    
    digit = theScore % 10L;         // Handle 1's digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[7], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[7] );
    #endif
}
 
//--------------------------------------------------------------  UpdateLevelNumbers
 
// Blah, blah, blah.  Just like the above functions but handles displaying theÉ
// level the player is on.  We allow 3 digits here (up to 999) before wrapping.
 
void UpdateLevelNumbers (void)
{
    short       digit;
    short       workNumber;
    
//  workNumber = levelOn;
    workNumber = oldFrameRate;
        
    digit = (workNumber + 1) / 100;     // Do 100's digit.
    digit = digit % 10L;
    if ((digit == 0) && ((workNumber + 1) < 1000))
        digit = 10;
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[8], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[3] );
    #endif
    
    digit = (workNumber + 1) / 10;          // Do 10's digit.
    digit = digit % 10L;
    if ((digit == 0) && ((workNumber + 1) < 100))
        digit = 10;
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[9], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[9] );
    #endif
    
    digit = (workNumber + 1) % 10;          // Do 1's digit.
    CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
            &((GrafPtr)backSrcMap)->portBits, 
            &numbersSrc[digit], &numbersDest[10], srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &numbersDest[10] );
    #endif
}
 
//--------------------------------------------------------------  GenerateLightning
 
// This function takes a point (h and v) and then generates two lightning boltsÉ
// (one from the tip of each obelisk) to the point.  It does this by generatingÉ
// a list of segments (as the lightning is broken up into segements).  The drawingÉ
// counterpart to this function will draw a line connecting these segements (a sortÉ
// of dot-to-dot).
 
void GenerateLightning (short h, short v)
{
    #define kLeftObeliskH       172
    #define kLeftObeliskV       250
    #define kRightObeliskH      468
    #define kRightObeliskV      250
    #define kWander             16
    
    short       i, leftDeltaH, rightDeltaH, leftDeltaV, rightDeltaV, range;
    
    leftDeltaH = h - kLeftObeliskH;             // Determine the h and v distances betweenÉ
    rightDeltaH = h - kRightObeliskH;           // obelisks and the target point.
    leftDeltaV = v - kLeftObeliskV;
    rightDeltaV = v - kRightObeliskV;
    
    for (i = 0; i < kNumLightningPts; i++)      // Calculate an even spread of points betweenÉ
    {                                           // obelisk tips and the target point.
        leftLightningPts[i].h = (leftDeltaH * i) / (kNumLightningPts - 1) + kLeftObeliskH;
        leftLightningPts[i].v = (leftDeltaV * i) / (kNumLightningPts - 1) + kLeftObeliskV;
        rightLightningPts[i].h = (rightDeltaH * i) / (kNumLightningPts - 1) + kRightObeliskH;
        rightLightningPts[i].v = (rightDeltaV * i) / (kNumLightningPts - 1) + kRightObeliskV;
    }
    
    range = kWander * 2 + 1;                    // Randomly scatter the points verticallyÉ
    for (i = 1; i < kNumLightningPts - 1; i++)  // but NOT the 1st or last points.
    {
        leftLightningPts[i].v += RandomInt(range) - kWander;
        rightLightningPts[i].v += RandomInt(range) - kWander;
    }
}
 
void DrawObelisks (void)
{   
    if ((lightningCount > 0) && evenFrame)      // Draw them "inverted"
    {
        CopyMask(   &((GrafPtr)obeliskSrcMap)->portBits,        // src
                    &((GrafPtr)obeliskMaskMap)->portBits,       // mask
                    &((GrafPtr) workSrcMap)->portBits,          // dst
                    &obeliskRects[0],                           // src rect
                    &obeliskRects[0],                           // mask rect
                    &obeliskRects[2]);                          // dst rect
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &obeliskRects[2] );
        #endif
 
        CopyMask(   &((GrafPtr)obeliskSrcMap)->portBits,        // src
                    &((GrafPtr)obeliskMaskMap)->portBits,       // mask
                    &((GrafPtr) workSrcMap)->portBits,          // dst
                    &obeliskRects[1],                           // src rect
                    &obeliskRects[1],                           // mask rect
                    &obeliskRects[3]);                          // dst rect
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &obeliskRects[3] );
        #endif
    }
    else
    {
        CopyMask(   &((GrafPtr)backSrcMap)->portBits,       // src
                    &((GrafPtr)obeliskMaskMap)->portBits,       // mask
                    &((GrafPtr) workSrcMap)->portBits,          // dst
                    &obeliskRects[2],                           // src rect
                    &obeliskRects[0],                           // mask rect
                    &obeliskRects[2]);                          // dst rect
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &obeliskRects[2] );
        #endif
 
        CopyMask(   &((GrafPtr)backSrcMap)->portBits,       // src
                    &((GrafPtr)obeliskMaskMap)->portBits,       // mask
                    &((GrafPtr) workSrcMap)->portBits,          // dst
                    &obeliskRects[3],                           // src rect
                    &obeliskRects[1],                           // mask rect
                    &obeliskRects[3]);                          // dst rect
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &obeliskRects[3] );
        #endif
    }
}
 
 
void StrikeLightningWork (void)
{
    short       i;
    GrafPtr     oldPort;
    RGBColor    yellow = {65535, 65535, 0};
    Rect        theRect;
    
    GetPort(&oldPort);
    SetPort((GrafPtr)workSrcMap);
    PenSize(1, 2);                              // Use a tall pen.
    RGBForeColor(&yellow);
                                                // Draw lightning bolts with inverted pen.
    MoveTo(leftLightningPts[0].h, leftLightningPts[0].v);
    for (i = 0; i < kNumLightningPts - 1; i++)  // Draw left lightning bolt.
    {
        MoveTo(leftLightningPts[i].h, leftLightningPts[i].v);
        LineTo(leftLightningPts[i + 1].h - 1, leftLightningPts[i + 1].v);
    }
    SetRect( &theRect, leftLightningPts[0].h, leftLightningPts[0].v, 
        leftLightningPts[kNumLightningPts].h - 1, leftLightningPts[kNumLightningPts].v );
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &theRect );
    #endif
    
    MoveTo(rightLightningPts[0].h, rightLightningPts[0].v);
    for (i = 0; i < kNumLightningPts - 1; i++)  // Draw right lightning bolt.
    {
        MoveTo(rightLightningPts[i].h, rightLightningPts[i].v);
        LineTo(rightLightningPts[i + 1].h - 1, rightLightningPts[i + 1].v);
    }
    SetRect( &theRect, rightLightningPts[0].h, rightLightningPts[0].v, 
        rightLightningPts[kNumLightningPts].h - 1, rightLightningPts[kNumLightningPts].v );
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &theRect );
    #endif
    
    PenNormal();                                // Return pen to normal.
    
    SetPort(oldPort);
}
 
//--------------------------------------------------------------  DumpBackToWorkMap
 
// Simple handy function that copies the entire background pixmap to theÉ
// work pixmap.
//--
 
//--------------------------------------------------------------  DumpBackToWorkMap
 
// Simple handy function that copies the entire background pixmap to theÉ
// work pixmap.
 
 
static inline void ClassicDumpBackToWork(void)
{
    // this is doing a full clean of the work map from the source map, don't inval
    // this rect.
    //
    // ¥ this will go away once DrawSprocket Underlays are used (in DR3 release)
    CopyBits(&((GrafPtr)backSrcMap)->portBits, 
            &((GrafPtr)workSrcMap)->portBits, 
            &backSrcRect, &backSrcRect, srcCopy, 0L);
}
 
// faster on some machines, on others it is slow because of dcbz
static inline void FastDumpBackToWorkMap (void)
{
    CGrafPtr srcGrafPtr = backSrcMap;
    PixMap *srcMap = *(srcGrafPtr->portPixMap);
    char *src = srcMap->baseAddr;
    UInt32 srcBump = srcMap->rowBytes & 0x3fff;
    
    CGrafPtr dstGrafPtr = workSrcMap;
    PixMap *dstMap = *(dstGrafPtr->portPixMap);
    char *dst = dstMap->baseAddr;
    UInt32 dstBump = dstMap->rowBytes & 0x3fff;
    
    int count;
    
    for(count = 0; count < 480; count++)
    {
        BlockMoveData(src,dst,640);
        
        src += srcBump;
        dst += dstBump;
    }
}
 
void DumpBackToWorkMap(void)
{   
    ClassicDumpBackToWork();
}
 
//--------------------------------------------------------------  QuickUnionRect
 
// The Mac Toolbox gives you a UnionRect() function, but, like any ToolboxÉ
// routine, if we can do it faster, we ought to.  Well, the function belowÉ
// is quick because (among other reasons), it assumes that the two rectsÉ
// being compared are the same size.
//
// Notes from Cary Farrier: Sorry John I don't agree with that advice.  You
// may be able to do something faster, but the speed gain you achieve may not
// be worth the effort required to develop, test, and debug the code.  John's
// advice is good for some scenarios, but it is the exception and not the
// rule.
void QuickUnionRect (Rect *rect1, Rect *rect2, Rect *whole)
{
    if (rect1->left < rect2->left)      // See if we're to use rect1's left.
    {
        whole->left = rect1->left;
        whole->right = rect2->right;
    }
    else                                // Use rect2's left.
    {
        whole->left = rect2->left;
        whole->right = rect1->right;
    }
    
    if (rect1->top < rect2->top)        // See if we're to use rect1's top.
    {
        whole->top = rect1->top;
        whole->bottom = rect2->bottom;
    }
    else                                // Use rect2's top.
    {
        whole->top = rect2->top;
        whole->bottom = rect1->bottom;
    }
}
 
//--------------------------------------------------------------  CheckPlayerWrapAround
 
// This handles drawing wrap-around.  It is such that, when a player walks partlyÉ
// off the right edge of the screen, you see the player peeking through on the leftÉ
// side of the screen.  Since we can't (shouldn't) assume that the physical screenÉ
// memory wraps around, we'll draw the right player clipped against the right edgeÉ
// of the screen and draw a SECOND PLAYER on the left edge (clipped to the left).
 
void CheckPlayerWrapAround (void)
{
    Rect        wrapRect, wasWrapRect, src;
    
    if (thePlayer.dest.right > 640)     // Player off right edge of screen.
    {
        thePlayer.wrapping = TRUE;      // Set "wrapping" flag.
        wrapRect = thePlayer.dest;      // Start out with copy of player bounds.
        wrapRect.left -= 640;           // Offset it a screenwidth to left.
        wrapRect.right -= 640;
                                        // Ditto with old location.
        wasWrapRect = thePlayer.wasDest;
        wasWrapRect.left -= 640;
        wasWrapRect.right -= 640;
        
        if (thePlayer.mode == kBones)   // Draw second bones.
        {
            src = playerRects[thePlayer.srcNum];
            src.bottom = src.top + thePlayer.frame;
            CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                    &((GrafPtr)playerMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        else                            // Draw second player (not bones).
        {
            CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                    &((GrafPtr)playerMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &playerRects[thePlayer.srcNum], 
                    &playerRects[thePlayer.srcNum], 
                    &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        thePlayer.wrap = wrapRect;
    }
    else if (thePlayer.dest.left < 0)   // Else if off the left edgeÉ
    {
        thePlayer.wrapping = TRUE;      // Set "wrapping" flag.
        wrapRect = thePlayer.dest;      // Start out with copy of player bounds.
        wrapRect.left += 640;           // Offset it a screenwidth to right.
        wrapRect.right += 640;
                                        // Ditto with old location.
        wasWrapRect = thePlayer.wasDest;
        wasWrapRect.left += 640;
        wasWrapRect.right += 640;
        
        if (thePlayer.mode == kBones)   // Draw second bones.
        {
            src = playerRects[thePlayer.srcNum];
            src.bottom = src.top + thePlayer.frame;
            CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                    &((GrafPtr)playerMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        else                            // Draw second player (not bones).
        {
            CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                    &((GrafPtr)playerMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &playerRects[thePlayer.srcNum], 
                    &playerRects[thePlayer.srcNum], 
                    &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        thePlayer.wrap = wrapRect;
    }
    else
        thePlayer.wrapping = FALSE;     // Otherwise, we're not wrapping.
}
 
//--------------------------------------------------------------  DrawTorches
 
// This handles drawing the two torch's flames.  It chooses randomly fromÉ
// 4 torch graphics and draws right over the old torches.
 
void DrawTorches (void)
{
    short       who;
    
    who = RandomInt(4);
    if (evenFrame)      // Only draw 1 torch - left on even framesÉ
    {
        CopyBits(&((GrafPtr)flameSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &flameRects[who], &flameDestRects[0], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &flameDestRects[0] );
        #endif
    }
    else                // and draw the right torch on odd frames.
    {                   // We do this even/odd thing for speed.  Why draw both?
        CopyBits(&((GrafPtr)flameSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &flameRects[who], &flameDestRects[1], srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &flameDestRects[1] );
        #endif
    }
}
 
//--------------------------------------------------------------  DrawHand
 
// This function takes care of drawing the hand offscreen.  There are onlyÉ
// two (well really three) choices - hand open, hand clutching (or no handÉ
// in which case both options are skipped).
 
void DrawHand (void)
{
    theHand.dest.right = theHand.dest.left + 56;
    theHand.dest.bottom = theHand.dest.top + 57;
 
    if (theHand.mode == kOutGrabeth)        // Fingers open.
    {
        CopyMask(&((GrafPtr)handSrcMap)->portBits, 
                &((GrafPtr)handMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &handRects[0], 
                &handRects[0], 
                &theHand.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &theHand.dest );
        #endif
    }
    else if (theHand.mode == kClutching)    // Fingers clenched.
    {
        CopyMask(&((GrafPtr)handSrcMap)->portBits, 
                &((GrafPtr)handMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &handRects[1], 
                &handRects[1], 
                &theHand.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &theHand.dest );
        #endif
    }
}
 
//--------------------------------------------------------------  DrawEye
 
// This function draws the eye (if it's floating about - stalking).
 
void DrawEye (void)
{
    if (theEye.mode == kStalking)
    {
        CopyMask(&((GrafPtr)eyeSrcMap)->portBits, 
                &((GrafPtr)eyeMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &eyeRects[theEye.srcNum], 
                &eyeRects[theEye.srcNum], 
                &theEye.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &theEye.dest );
        #endif
    }
}
 
//--------------------------------------------------------------  DrawPlayer
 
// Although called "DrawPlayer()", this function actually does its drawingÉ
// offscreen.  It is the above routine that will finally copy our offscreenÉ
// work to the main screen.  Anyway, the below function draws the playerÉ
// offscreen in the correct position and state.
 
void DrawPlayer (void)
{
    Rect        src;
    
    if ((evenFrame) && (thePlayer.mode == kIdle))
    {           // On even frames, we'll draw the "flashed" graphic of the player.
                // If you've played Glypha, you notice that the player begins aÉ
                // game flashing alternately between bones and a normal player.
        CopyMask(&((GrafPtr)idleSrcMap)->portBits, 
                &((GrafPtr)playerMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &idleSrcRect, 
                &playerRects[thePlayer.srcNum], 
                &thePlayer.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &thePlayer.dest );
        #endif
    }
    else if (thePlayer.mode == kBones)
    {           // If the player is dead and a pile of bonesÉ
        src = playerRects[thePlayer.srcNum];
        src.bottom = src.top + thePlayer.frame;
        CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                &((GrafPtr)playerMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &src, &src, &thePlayer.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &thePlayer.dest );
        #endif
    }
    else        // Else, if the player is neither idle nor deadÉ
    {
        CopyMask(&((GrafPtr)playerSrcMap)->portBits, 
                &((GrafPtr)playerMaskMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &playerRects[thePlayer.srcNum], 
                &playerRects[thePlayer.srcNum], 
                &thePlayer.dest);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &thePlayer.dest );
        #endif
    }
                // Now we add the player to the update rect list.
                // Record old locations.
    thePlayer.wasH = thePlayer.h;
    thePlayer.wasV = thePlayer.v;
                // Record old bounds rect.
    thePlayer.wasDest = thePlayer.dest;
}
 
//--------------------------------------------------------------  CheckEnemyWrapAround
 
// This function both determines whether or not an enemy (sphinx) is wrapping around.
// If it is, the "second" wrapped-around enemy is drawn.
 
void CheckEnemyWrapAround (short who)
{
    Rect        wrapRect, wasWrapRect, src;
    
    if (theEnemies[who].dest.right > 640)   // Is enemy off the right edge of screen?
    {
        wrapRect = theEnemies[who].dest;    // Copy bounds.
        wrapRect.left -= 640;               // Offset bounds copy to left (one screen width).
        wrapRect.right -= 640;
                                            // Ditto with old bounds.
        wasWrapRect = theEnemies[who].wasDest;
        wasWrapRect.left -= 640;
        wasWrapRect.right -= 640;
                                            // Handle "egg" enemies.
        if ((theEnemies[who].mode == kFalling) || (theEnemies[who].mode == kEggTimer))
        {                                   // Handle "egg" enemy sinking into platform.
            if ((theEnemies[who].mode == kEggTimer) && (theEnemies[who].frame < 24))
            {
                src = eggSrcRect;
                src.bottom = src.top + theEnemies[who].frame;
            }
            else
                src = eggSrcRect;
            CopyMask(&((GrafPtr)eggSrcMap)->portBits, 
                    &((GrafPtr)eggMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        else                                // Otherwise, if enemy not an eggÉ
        {
            CopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
                    &((GrafPtr)enemyFlyMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &enemyRects[theEnemies[who].srcNum], 
                    &enemyRects[theEnemies[who].srcNum], 
                    &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
    }
    else if (theEnemies[who].dest.left < 0) // Check to see if enemy off left edge instead.
    {
        wrapRect = theEnemies[who].dest;    // Make a copy of enemy bounds.
        wrapRect.left += 640;               // Offset it right one screens width.
        wrapRect.right += 640;
                                            // Ditto with old bounds.
        wasWrapRect = theEnemies[who].wasDest;
        wasWrapRect.left += 640;
        wasWrapRect.right += 640;
        if ((theEnemies[who].mode == kFalling) || (theEnemies[who].mode == kEggTimer))
        {                                   // Blah, blah, blah.  This is just like the above.
            if ((theEnemies[who].mode == kEggTimer) && (theEnemies[who].frame < 24))
            {
                src = eggSrcRect;
                src.bottom = src.top + theEnemies[who].frame;
            }
            else
                src = eggSrcRect;
            CopyMask(&((GrafPtr)eggSrcMap)->portBits, 
                    &((GrafPtr)eggMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
        else
        {
            CopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
                    &((GrafPtr)enemyFlyMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &enemyRects[theEnemies[who].srcNum], 
                    &enemyRects[theEnemies[who].srcNum], 
                    &wrapRect);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &wrapRect );
            #endif
        }
    }
}
 
//--------------------------------------------------------------  DrawEnemies
 
// This function draws all the sphinx enemies (or eggs if they're in that state).
// It doesn't handle wrap-around (the above function does) but it does call it.
 
void DrawEnemies (void)
{
    Rect        src;
    short       i;
    
    for (i = 0; i < numEnemies; i++)    // Go through all enemies.
    {
        switch (theEnemies[i].mode)     // Handle the different modes as seperate cases.
        {
            case kSpawning:             // Spawning enemies are "growing" out of the platform.
            src = enemyRects[theEnemies[i].srcNum];
            src.bottom = src.top + theEnemies[i].frame;
            CopyMask(&((GrafPtr)enemyWalkSrcMap)->portBits, 
                    &((GrafPtr)enemyWalkMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &theEnemies[i].dest);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &theEnemies[i].dest );
            #endif
                    
                                        // Don't need to check wrap-around, when enemiesÉ
                                        // spawn, they're never on the edge of screen.
            theEnemies[i].wasDest = theEnemies[i].dest;
            theEnemies[i].wasH = theEnemies[i].h;
            theEnemies[i].wasV = theEnemies[i].v;
            break;
            
            case kFlying:               // Flying enemies are air borne (gee).
            CopyMask(&((GrafPtr)enemyFlySrcMap)->portBits, 
                    &((GrafPtr)enemyFlyMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &enemyRects[theEnemies[i].srcNum], &enemyRects[theEnemies[i].srcNum], 
                    &theEnemies[i].dest);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &theEnemies[i].dest );
            #endif
                    
            CheckEnemyWrapAround(i);    // I like the word "air bourne".
            theEnemies[i].wasDest = theEnemies[i].dest;
            theEnemies[i].wasH = theEnemies[i].h;
            theEnemies[i].wasV = theEnemies[i].v;
            break;
            
            case kWalking:              // Walking enemies are walking.  Enemies.
            CopyMask(&((GrafPtr)enemyWalkSrcMap)->portBits, 
                    &((GrafPtr)enemyWalkMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &enemyRects[theEnemies[i].srcNum], &enemyRects[theEnemies[i].srcNum], 
                    &theEnemies[i].dest);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &theEnemies[i].dest );
            #endif
                                        // Don't need to check wrap-around, enemies walkÉ
                                        // only briefly, and never off edge of screen.
            theEnemies[i].wasDest = theEnemies[i].dest;
            theEnemies[i].wasH = theEnemies[i].h;
            theEnemies[i].wasV = theEnemies[i].v;
            break;
            
            case kFalling:              // Falling enemies are in fact eggs!
            CopyMask(&((GrafPtr)eggSrcMap)->portBits, 
                    &((GrafPtr)eggMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &eggSrcRect, &eggSrcRect, &theEnemies[i].dest);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &theEnemies[i].dest );
            #endif
                    
            CheckEnemyWrapAround(i);    // Check for wrap around.
            theEnemies[i].wasDest = theEnemies[i].dest;
            theEnemies[i].wasH = theEnemies[i].h;
            theEnemies[i].wasV = theEnemies[i].v;
            break;
            
            case kEggTimer:             // These are idle, perhaps hatching, eggs.
            if (theEnemies[i].frame < 24)
            {                           // Below countdown = 24, the egss are sinkingÉ
                src = eggSrcRect;       // into the platform (hatch time!).
                src.bottom = src.top + theEnemies[i].frame;
            }
            else
                src = eggSrcRect;
            CopyMask(&((GrafPtr)eggSrcMap)->portBits, 
                    &((GrafPtr)eggMaskMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &src, &src, &theEnemies[i].dest);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &theEnemies[i].dest );
            #endif
                    
            CheckEnemyWrapAround(i);    // Check for wrap around.
            theEnemies[i].wasDest = theEnemies[i].dest;
            theEnemies[i].wasH = theEnemies[i].h;
            theEnemies[i].wasV = theEnemies[i].v;
            break;
        }
    }
}
 
//------------- DrawBanner
void DrawBanner (void)
{
    Rect destRect;
    Rect srcRect;
    long start = (TickCount() / 2) % 1280;
    long seg1Len;
    long seg2Len;
    
    if (start <= 640) 
    {
        seg1Len = 640;
        seg2Len = 0;
    }
    else
    {
        seg1Len = 1280 - start;
        seg2Len = 640 - seg1Len;
    }
    
    SetRect(&destRect,
            0,      // left
            460,    // top
            seg1Len,    // right
            480);   // bottom
    
            
    SetRect(&srcRect,
            start,
            0,
            start + seg1Len,
            20);
            
            
    CopyBits(   &((GrafPtr)bannerSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &srcRect, &destRect, srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &destRect );
    #endif
                
    if (seg2Len > 0)
    {
        destRect.left = seg1Len;
        destRect.right = 640;
        srcRect.left = 0;
        srcRect.right = seg2Len;
        
        CopyBits(   &((GrafPtr)bannerSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &srcRect, &destRect, srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &destRect );
    #endif
    }
 
}
 
//--------------------------------------------------------------  DrawPixelShatter
 
void DrawPixelShatter (void)
{
    int i;
    CGrafPtr theGrafPtr = workSrcMap;
    PixMap *theMap = *(theGrafPtr->portPixMap);
    short h,v;
    short rowBytes = theMap->rowBytes & 0x3fff;
    short minH = 1000, minV = 1000, maxH = 0, maxV = 0;
    Rect theRect;
    
    for(i = 0; i < numPixelShatter; i++)
    {
        char *addr = theMap->baseAddr;
        int offset = 0;
                
        h = thePixelShatter[i].h;
        v = thePixelShatter[i].v;
        if( h < minH )
            minH = h;
        if( h > maxH )
            maxH = h;
        if( v < minV )
            minV = v;
        if( v > maxV )
            maxV = v;
            
        h >>= 4;
        v >>= 4;
        
        offset = rowBytes * v;
        offset += h;
        
        addr += offset;
        
        *addr = thePixelShatter[i].color;
    }
    
    SetRect( &theRect, minH, minV, maxH, maxV );
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &theRect );
    #endif
    
}
 
//--------------------------------------------------------------  DrawLava
void DrawLava(void);
#if 0
void DrawLava(void)
{
    short where = RandomInt(640);
    short velocity = -(RandomInt(15) + 15);
 
    StartPixelShatter(  where, 460, 0, velocity, kShatterLavaBubble);
}
#endif
 
void DrawScoreFloaters(void);
void DrawScoreFloaters(void)
{
#if 0
    int i,j,count;
    Rect r;
    CGrafPtr theGrafPtr = workSrcMap;
    PixMap *theMap = *(theGrafPtr->portPixMap);
    short rowBytes = theMap->rowBytes & 0x3fff;
    char *addr = theMap->baseAddr;
    
    for(count = 0; count < numScoreFloater; count++)
    {
        r.top = theScoreFloater[count].location.v;
        r.left = theScoreFloater[count].location.h;
        r.bottom = theScoreFloater[count].location.v + 20;
        r.right = theScoreFloater[count].location.h + 60;
    
        addr = theMap->baseAddr + (r.top * rowBytes);
        
        for(j = r.top; j < r.bottom; j++)
        {
            for(i = r.left; i < r.right; i++)
            {
                addr[i] = 0;
            }
            
            addr += rowBytes;
        }
    }
#else
    int count;
    Rect baseRect = {0,0,8,11};
        
    for(count = 0; count < numScoreFloater; count++)
    {
        short       digit;
        short       workNumber;
        Rect r = baseRect;
                
        r.top += theScoreFloater[count].location.v;
        r.bottom += theScoreFloater[count].location.v;
        r.left += theScoreFloater[count].location.h;
        r.right += theScoreFloater[count].location.h;
        workNumber = theScoreFloater[count].score;
            
        digit = (workNumber + 1) / 1000;        // Do 1000's digit.
        digit = digit % 10L;
        if (digit != 0)
        {
            CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &numbersSrc[digit], &r, srcCopy, 0L);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &r );
            #endif
        }
        
        r.left += 11;
        r.right += 11;
 
        digit = (workNumber + 1) / 100;     // Do 100's digit.
        digit = digit % 10L;
        if ((digit != 0) || (workNumber >= 1000))
        {
            CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &numbersSrc[digit], &r, srcCopy, 0L);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &r );
            #endif
        }
 
        r.left += 11;
        r.right += 11;
                
        digit = (workNumber + 1) / 10;          // Do 10's digit.
        digit = digit % 10L;
        if ((digit != 0) || (workNumber >= 100))
        {
            CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
                    &((GrafPtr)workSrcMap)->portBits, 
                    &numbersSrc[digit], &r, srcCopy, 0L);
            #ifdef USE_INVALID_RECTS
            DSpContext_InvalBackBufferRect( gTheContext, &r );
            #endif
        }
        
        r.left += 11;
        r.right += 11;
 
        digit = (workNumber) % 10;          // Do 1's digit.
        CopyBits(&((GrafPtr)numberSrcMap)->portBits, 
                &((GrafPtr)workSrcMap)->portBits, 
                &numbersSrc[digit], &r, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &r );
        #endif
    }
#endif
}
 
//--------------------------------------------------------------  DrawFrame
 
// This function is the "master" drawing function that calls all the aboveÉ
// routines.  It is called once per frame.
 
void DrawFrame (void)
{
    Boolean bannerInFront = false;
    
    {
        KeyMap theKeys;
        GetKeys(theKeys);
    
        if ((theKeys[1] & 0x004))
        {
            bannerInFront = true;
        }
    }
 
    if (!bannerInFront)
    {
        DrawBanner();
    }
 
    DrawTorches();              // Gee, draws the torches?
    
    DrawScoreFloaters();
    
    DrawHand();                 // Draws the hand?
    DrawEye();                  // A clue to easing your documentation demandsÉ
    DrawPlayer();               // is to use "smart" names for your functions.
    CheckPlayerWrapAround();    // Check for player wrap-around.
    DrawEnemies();              // Handle all sphinx-type enemy drawing.
 
    DrawObelisks();         // draw the obelisks
    DrawPixelShatter();         // draw exploding pixels
    
    DrawLava();                 // add sparkling lava
 
    if (bannerInFront)
    {
        DrawBanner();
    }   
 
}
 
//--------------------------------------------------------------  GameQuitGraphics
 
void GameQuitGraphics(void)
{
    short h,v;
    
    for(v = 0; v < 480; v += 24)
    {
        for(h = 0; h < 640; h += 32)
        {
                StartPixelShatter(h, v, 0, 0, kShatterLightningDust);
        }
    }
 
    CopyBits(   &((GrafPtr)origBackSrcMap)->portBits, 
                &((GrafPtr)backSrcMap)->portBits, 
                &backSrcRect, &backSrcRect, srcCopy, 0L);
    #ifdef USE_INVALID_RECTS
    DSpContext_InvalBackBufferRect( gTheContext, &backSrcRect );
    #endif
}
 
//--------------------------------------------------------------  GameIdleAnimation
 
void GameIdleAnimation(void)
{
    // do our idle animation
    DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
    DumpBackToWorkMap();                    // clear the screen
    DrawBanner();
    DrawTorches();
    HandlePixelShatter();
    HandleLava();
    DrawLava();
    
    if (helpOpen)
    {
        CopyBits(&((GrafPtr)helpSrcMap)->portBits, 
            &((GrafPtr)workSrcMap)->portBits, 
            &helpSrc, &helpDest, srcCopy, 0L);
        #ifdef USE_INVALID_RECTS
        DSpContext_InvalBackBufferRect( gTheContext, &helpDest );
        #endif
    }
    
    DrawPixelShatter();
    HandleLightning();
 
    DSpContext_SwapBuffers( gTheContext, NULL, NULL );  
}