home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / Samples / SprocketExamples / GlyphaIV / GlyphaIV Sources / G4Play.c < prev    next >
Encoding:
Text File  |  1998-07-14  |  54.7 KB  |  1,740 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        G4Play.c
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Version:    xxx put version here xxx
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     File Ownership:
  11.  
  12.         DRI:                xxx put dri here xxx
  13.  
  14.         Other Contact:        xxx put other contact here xxx
  15.  
  16.         Technology:            xxx put technology here xxx
  17.  
  18.     Writers:
  19.  
  20.         (BWS)    Brent Schorsch
  21.         (sjb)    Steve Bollinger
  22.  
  23.     Change History (most recent first):
  24.  
  25.          <5>      7/1/98    BWS        fix comment
  26.          <4>      7/1/98    sjb        Update to CWPro 2
  27.          <3>     6/18/98    sjb        InputSprocket.h comes from <> place
  28. */
  29.  
  30.  
  31. //============================================================================
  32. //----------------------------------------------------------------------------
  33. //                                    Play.c
  34. //----------------------------------------------------------------------------
  35. //============================================================================
  36.  
  37. // This (rather large) file handles all player routines while a game is in…
  38. // progress. It gets the player's input, moves the player, tests for collisions…
  39. // and generally handles the "main game loop".  Enemies and actually drawing…
  40. // the graphics to the screen are handled in other files.
  41.  
  42. #include "G4Externs.h"
  43.  
  44. #if GENERATINGPOWERPC
  45.     #include <InputSprocket.h>
  46. #endif
  47.  
  48. #include <ToolUtils.h>
  49. #include <TextUtils.h>
  50. #include <Timer.h>
  51.  
  52. #define kFlapImpulse            48
  53. #define kGlideImpulse            12
  54. #define kAirResistance            2
  55. #define kMaxHVelocity            192
  56. #define kMaxVVelocity            512
  57. #define kNumLightningStrikes    5
  58.  
  59.  
  60. void SetUpLevel (void);
  61. void ResetPlayer (Boolean);
  62. void OffAMortal (void);
  63. void DoCommandKey (void);
  64. void GetPlayerInput (void);
  65. void HandlePlayerIdle (void);
  66. void HandlePlayerFlying (void);
  67. void HandlePlayerWalking (void);
  68. void HandlePlayerSinking (void);
  69. void HandlePlayerFalling (void);
  70. void HandlePlayerBones (void);
  71. void MovePlayer (void);
  72. void CheckTouchDownCollision (void);
  73. void CheckPlatformCollision (void);
  74. void KeepPlayerOnPlatform (void);
  75. void CheckLavaRoofCollision (void);
  76. void SetAndCheckPlayerDest (void);
  77. void HandleLightning (void);
  78. void FinishLightning (void);
  79. void HandleCountDownTimer (void);
  80. void CheckHighScore (void);
  81.  
  82. void HandleLava(void);
  83. void DrawLava(void);
  84.  
  85. playerType    thePlayer;
  86. enemyType    theEnemies[kMaxEnemies];
  87. KeyMap        theKeys;
  88. Rect        platformRects[6], touchDownRects[6], enemyRects[24];
  89. Rect        enemyInitRects[5];
  90. long        theScore, wasTensOfThousands;
  91. short        numLedges, beginOnLevel, levelOn, livesLeft, lightH, lightV;
  92. short        lightningCount, numEnemies, countDownTimer;
  93. Boolean        playing, pausing, flapKeyDown, evenFrame;
  94. Boolean        doEnemyFlapSound, doEnemyScrapeSound;
  95.  
  96. extern    handInfo    theHand;
  97. extern    prefsInfo    thePrefs;
  98. extern    Rect        playerRects[11], mainWindowRect;
  99. extern    short        numOwls;
  100. extern    Boolean        quitting, openTheScores;
  101.  
  102. extern CGrafPtr workSrcMap;
  103.  
  104.  
  105. short oldFrameRate;
  106. short currentFrameRate;
  107. UnsignedWide frameTime;
  108.  
  109. short numScoreFloater = 0;
  110. scoreFloaterType theScoreFloater[kMaxScoreFloater];
  111.  
  112.  
  113.  
  114. //
  115.  
  116. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button);
  117. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button)
  118. {
  119.     // remember a little across calls
  120.     static ISpElementReference lastXAxis = 0;
  121.     static Boolean lastLeft = false;
  122.     static Boolean lastRight = false;
  123.     static Boolean lastButton = false;
  124.  
  125.     int maxEvents = 10;        // process only ten events per round
  126.     
  127.     ISpElementEvent theEvent;            // our event
  128.     Boolean wasEvent = true;
  129.     OSErr err = noErr;
  130.     ISpElementListReference globalList;
  131.     ISpElementInfo info;
  132.     
  133.     *left = lastLeft;
  134.     *right = lastRight;
  135.     *button = lastButton;
  136.  
  137.  
  138.     if (ISpGetGlobalElementList == nil)
  139.     {
  140.         return -1;
  141.     }
  142.  
  143.     err = ISpGetGlobalElementList(&globalList);
  144.  
  145.     while(maxEvents > 0)
  146.     {
  147.         err = ISpElementList_GetNextEvent(globalList, sizeof(theEvent), &theEvent, &wasEvent);
  148.         
  149.         if ((wasEvent == false) || (err))
  150.         {
  151.             break;
  152.         }
  153.         
  154.         ISpElement_GetInfo(theEvent.element, &info);
  155.  
  156.         // get information about this element since we are not doing any configuration
  157.         // we need to know the about labels and kinds
  158.  
  159.         switch(info.theKind)
  160.         {
  161.             case kISpElementKind_Button:
  162.             {
  163.                 Boolean newValue;
  164.                 
  165.                 // figure out the new value for the boolean
  166.                 if (theEvent.data)
  167.                 {
  168.                     newValue = true;
  169.                 }
  170.                 else
  171.                 {
  172.                     newValue = false;
  173.                 }
  174.     
  175.                 *button = newValue;
  176.             }
  177.             break;
  178.             
  179.             case kISpElementKind_DPad:
  180.             {
  181.                 // we'll support using any DPad to move
  182.                 if (1) // (info.theLabel == kISpElementLabel_Pad_Move || info.theLabel == kISpElementLabel_Pad_Move_Horiz)
  183.                 {
  184.                     // a movement DPad so they might be using
  185.                     // a console style direction pad so turn
  186.                     // that into axis style data
  187.                     
  188.                     lastXAxis = nil;
  189.                     
  190.                     switch(theEvent.data)
  191.                     {
  192.                         case kISpPadIdle:
  193.                         case kISpPadUp:
  194.                         case kISpPadDown:            
  195.                             *left = false;
  196.                             *right = false;
  197.                             break;
  198.                         case kISpPadLeft:
  199.                         case kISpPadUpLeft:
  200.                         case kISpPadDownLeft:
  201.                             *left = true;
  202.                             *right = false;
  203.                             break;
  204.                         case kISpPadUpRight:
  205.                         case kISpPadRight:
  206.                         case kISpPadDownRight:
  207.                             *left = false;
  208.                             *right = true;
  209.                             break;
  210.                     }
  211.                 }
  212.             }
  213.             break;
  214.             
  215.             case kISpElementKind_Axis:
  216.             {
  217.                 // if it is an axis find out if it is the
  218.                 // x or y axis style data and use that.
  219.                 
  220.                 if (info.theLabel == kISpElementLabel_Axis_XAxis || info.theLabel == kISpElementLabel_Axis_Rudder)
  221.                 {
  222.                     lastXAxis = theEvent.element;
  223.                 }
  224.                 
  225.                 ISpElement_Flush(theEvent.element);
  226.             }
  227.             break;
  228.         }
  229.         
  230.         maxEvents--;
  231.     }
  232.     
  233.     // poll the last meaningful axis we had on the way out
  234.     // just to make sure we had good data
  235.     if (lastXAxis != nil)
  236.     {
  237.         UInt32 xAxis;
  238.         double calc;
  239.         
  240.         *left = false;
  241.         *right = false;
  242.     
  243.         ISpElement_GetSimpleState(lastXAxis, &xAxis);
  244.  
  245.         calc = xAxis;
  246.         calc -= kISpAxisMiddle;
  247.         calc /= kISpAxisMaximum;
  248.         
  249.         if (calc > 0.2)
  250.         {
  251.             *right = true;
  252.         }
  253.         else if (calc < -0.2)
  254.         {
  255.             *left = true;
  256.         }
  257.     }
  258.     
  259.     return err;
  260. }
  261.  
  262. //==============================================================  Functions
  263. //--------------------------------------------------------------  InitNewGame
  264.  
  265. // This funciton sets up variables and readies for a new game.  It is called…
  266. // only when a the user selects "New Game" - during the course of a game, it…
  267. // is  not called again.
  268.  
  269. void InitNewGame (void)
  270. {                                // Initialize a number of game variables.
  271.     countDownTimer = 0;            // Zero count down timer.
  272.     numLedges = 3;                // Initial number of ledges (platforms).
  273.     beginOnLevel = 1;            // Ledge (platform) the player is on (center ledge).
  274.     levelOn = 0;                // Game level on (first level).
  275.     livesLeft = kInitNumLives;    // Number of player lives remaining.
  276.     theScore = 0L;                // Player's score (a long - can go to 2 billion).
  277.     playing = TRUE;                // Flag playing.
  278.     pausing = FALSE;            // Not paused.
  279.     evenFrame = TRUE;            // Set an initial state for evenFrame.
  280.     wasTensOfThousands = 0L;    // Used for noting when player gets an extra life.
  281.     numOwls = 4;                // Number of "owl" enemies for this level.
  282.     
  283.     numPixelShatter = 0;        // no pixels exploding currently
  284.     numScoreFloater = 0;        // no score floaters
  285.     
  286.     InitHandLocation();            // Get the mummy hand down in the lava.
  287.     theHand.mode = kLurking;    // Flag the hand in "lurking" mode.
  288.     
  289.     ResetPlayer(TRUE);            // Initialize all player variables and put on ledge.
  290.     UpdateLivesNumbers();        // Display number of lives remaining on screen.
  291.     UpdateScoreNumbers();        // Display the player's score (zero at this point).
  292.     UpdateLevelNumbers();        // Display the level (wave) the player is on.
  293.  
  294.     DumpBackToWorkMap();        // Copy background offscreen to "work" offscreen.
  295.  
  296.     SetUpLevel();                // Set up platforms for first level (wave).
  297.     
  298.     GenerateEnemies();            // Prepare all enemies for this level.
  299. }
  300.  
  301. //--------------------------------------------------------------  SetUpLevel
  302.  
  303. // Primarily, this function is called to set up the ledges for the…
  304. // current level (wave) the player is on.  It determines how many…
  305. // are required and then draws these offscreen.  It also flashes…
  306. // the obelisks and strikes the lightning.
  307.  
  308. void SetUpLevel (void)
  309. {
  310.     short        wasLedges, waveMultiple;
  311.     Boolean validLightning[6];
  312.     int invalidLightning;
  313.     
  314.     for(invalidLightning = 0; invalidLightning < 6; invalidLightning++)
  315.     {
  316.         validLightning[invalidLightning] = false;
  317.     }
  318.     
  319.     KillOffEye();                    // Return eye to the aether.
  320.     
  321.     wasLedges = numLedges;            // Remember number of ledges.
  322.     waveMultiple = levelOn % 5;        // Waves repeat every 5th wave (but harder!).
  323.     
  324.     switch (waveMultiple)            // See which of the 5 we're on.
  325.     {
  326.         case 0:                        // Waves 0, 5, 10, …
  327.         numLedges = 5;                // have 5 ledges (platforms) on screen.
  328.         validLightning[5] = true;
  329.         break;
  330.         
  331.         case 1:                        // Waves 1, 6, 11, …
  332.         numLedges = 6;                // are up to 6 ledges (platforms) on screen.
  333.         validLightning[5] = true;
  334.         break;
  335.         
  336.         case 2:                        // Waves 2, 7, 12, …
  337.         numLedges = 5;                // return to 5 ledges (platforms) on screen.
  338.         validLightning[5] = true;
  339.         break;
  340.         
  341.         case 3:                        // Waves 3, 8, 13, …
  342.         numLedges = 3;                // drop to 3 ledges (platforms) on screen.
  343.         validLightning[3] = true;
  344.         validLightning[4] = true;
  345.         break;
  346.         
  347.         case 4:                        // Waves 4, 9, 14, …
  348.         numLedges = 6;                // and return to 6 ledges (platforms) on screen.
  349.         validLightning[3] = true;
  350.         validLightning[4] = true;
  351.         validLightning[5] = true;
  352.         break;
  353.     }
  354.     
  355.     if (wasLedges != numLedges)        // No need to redraw if platforms are unchanged.
  356.         DrawPlatforms(numLedges);
  357.  
  358. #if 0
  359.                             // These are the platforms.  See diagram for numbering.
  360.     SetRect(&platformRects[0], 206, 424, 433, 438);        //_______________
  361.     SetRect(&platformRects[1], -256, 284, 149, 298);    //
  362.     SetRect(&platformRects[2], 490, 284, 896, 298);        //--3--     --4--
  363.     SetRect(&platformRects[3], -256, 105, 149, 119);    //     --5--
  364.     SetRect(&platformRects[4], 490, 105, 896, 119);        //--1--     --2--
  365.     SetRect(&platformRects[5], 233, 190, 407, 204);        //_____--0--_____
  366.  
  367. #endif
  368.  
  369.  
  370.  
  371.     {
  372.         int toast;
  373.         
  374.         if (levelOn == 0)
  375.         {
  376.             for(toast = 0; toast < 6; toast++)
  377.             {
  378.                 validLightning[toast] = true;
  379.             }
  380.         }
  381.         
  382.         for(toast = 0; toast < 6; toast++)
  383.         {
  384.             Rect r = platformRects[toast];
  385.             int diff = (r.right - r.left) / 4;
  386.             int subtoast;
  387.             if (!validLightning[toast])
  388.             {
  389.                 continue;
  390.             }
  391.             
  392.             r.right = r.left + diff;
  393.             
  394.             for(subtoast = 0; subtoast < 4; subtoast++)
  395.             {
  396.                 unsigned long tickWait = TickCount() + 3L;
  397.                 
  398.                 
  399.                 PlayExternalSound(kLightningSound, kLightningPriority);
  400.     
  401.                 DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
  402.                 GetPlayerInput();                        // Get the player's input (keystrokes).
  403.                 MovePlayer();                            // Move the player's bird.
  404.                 
  405.                 HandleLava();
  406.                 DumpBackToWorkMap();                    // clear the screen
  407.                 DrawBanner();
  408.                 DrawLava();
  409.                 DrawPlayer();
  410.  
  411.                 // lightning
  412.                 lightningCount = 1;                        
  413.                 lightH = (r.left + r.right) / 2;
  414.                 lightV = (r.top + r.bottom) / 2;
  415.                 HandleLightning();
  416.  
  417.                 // pixel explosions
  418.                 StartPixelShatterRect(&r, 0, 0, kShatterPlatformLightning);
  419.                 HandlePixelShatter();
  420.                 DrawPixelShatter();
  421.                 
  422.                 DSpContext_SwapBuffers( gTheContext, NULL, NULL );
  423.  
  424.                 r.left += diff;
  425.                 r.right += diff;
  426.  
  427.                 evenFrame = !evenFrame;
  428.                 
  429.                 do                // Wait for 2 Ticks to pass to keep fast Macs at bay.
  430.                 {
  431.                 }
  432.                 while (TickCount() < tickWait);
  433.             }
  434.         }
  435.     }
  436.     
  437.     
  438.     UpdateLevelNumbers();            // Display the current level on screen.
  439. }
  440.  
  441. //--------------------------------------------------------------  ResetPlayer
  442.  
  443. // This function prepares the player - it places the player and his/her mount…
  444. // in their proper starting location (depending on which platform they are to…
  445. // begin on), and it sets all the player's variables to their initial state.
  446.  
  447. void ResetPlayer (Boolean initialPlace)
  448. {
  449.     short        location;
  450.     
  451.     thePlayer.srcNum = 5;            // Set which graphic (frame) the player is to use.
  452.     thePlayer.frame = 320;            // This variable will be used as a coutndown timer.
  453.     
  454.     if (initialPlace)                // If "initialPlace" is TRUE, …
  455.         location = 0;                // the player is to begin on the lowest platform.
  456.     else                            // Otherwise, a random location is chosen.
  457.         location = RandomInt(numLedges);
  458.     
  459.     switch (location)                // Move player horizontally and vertically to their…
  460.     {                                // proper location (based on ledge # they're on).
  461.         case 0:
  462.         thePlayer.h = 296 << 4;        // Bottom center ledge.
  463.         thePlayer.v = 377 << 4;        // We're scaling by 16.
  464.         break;
  465.         
  466.         case 1:
  467.         thePlayer.h = 102 << 4;        // Lower left ledge.
  468.         thePlayer.v = 237 << 4;
  469.         break;
  470.         
  471.         case 2:
  472.         thePlayer.h = 489 << 4;        // Lower right ledge.
  473.         thePlayer.v = 237 << 4;
  474.         break;
  475.         
  476.         case 3:
  477.         thePlayer.h = 102 << 4;        // Top left ledge.
  478.         thePlayer.v = 58 << 4;
  479.         break;
  480.         
  481.         case 4:
  482.         thePlayer.h = 489 << 4;        // Top right ledge.
  483.         thePlayer.v = 58 << 4;
  484.         break;
  485.         
  486.         case 5:
  487.         thePlayer.h = 296 << 4;        // Top central ledge.
  488.         thePlayer.v = 143 << 4;
  489.         break;
  490.     }
  491.                                     // Assign destination rectangle.
  492.     thePlayer.dest = playerRects[thePlayer.srcNum];
  493.     ZeroRectCorner(&thePlayer.dest);
  494.     OffsetRect(&thePlayer.dest, thePlayer.h >> 4, thePlayer.v >> 4);
  495.     thePlayer.wasDest = thePlayer.dest;
  496.     
  497.     thePlayer.hVel = 0;                // Player initially has no velocity.
  498.     thePlayer.vVel = 0;
  499.     thePlayer.facingRight = TRUE;    // We're facing to the right.
  500.     thePlayer.flapping = FALSE;        // We're not flapping our wings initially.
  501.     thePlayer.wrapping = FALSE;        // We can't be wrapping around the edge of the screen.
  502.     thePlayer.clutched = FALSE;        // The hand ain't got us.
  503.     thePlayer.mode = kIdle;            // Our mode is "idle" - waiting to be "born".
  504.     if (lightningCount == 0)        // Prepare for a lightning display to "birth" us.
  505.     {
  506.         lightH = thePlayer.dest.left + 24;
  507.         lightV = thePlayer.dest.bottom - 24;
  508.         lightningCount = kNumLightningStrikes;
  509.     }
  510. }
  511.  
  512. //--------------------------------------------------------------  OffAMortal
  513.  
  514. // Alas, 'tis here that a player is brought who loses a life.
  515.  
  516. void OffAMortal (void)
  517. {
  518.     livesLeft--;                // Decrememnt number of player lives left.
  519.     
  520.     if (livesLeft > 0)            // Indeed, are there lives remaining?
  521.     {
  522.         ResetPlayer(FALSE);        // Good, start a new one off.
  523.         UpdateLivesNumbers();    // Make note of the number of lives remaining.
  524.     }
  525.     else                        // Otherwise, we are at the dreaded "Game Over".
  526.         playing = FALSE;        // Set flag to drop us out of game loop.
  527. }
  528.  
  529. //--------------------------------------------------------------  DoCommandKey
  530.  
  531. // This function handles the case when the user has held down the command…
  532. // key.  Note, this only applies to input when a game is in session - otherwise…
  533. // a standard event loop handles command keys and everything else.
  534.  
  535. void DoCommandKey (void)
  536. {
  537.     if (BitTst(&theKeys, kEKeyMap))            // Test for "command - E"…
  538.     {
  539.         playing = FALSE;                    // which would indicate "End Game".
  540.     }
  541.     else if (BitTst(&theKeys, kPKeyMap))    // Otherwise, see if it's "command - P".
  542.     {
  543.         pausing = TRUE;                        // This means the player is pausing the game.
  544.         MenusReflectMode();                    // Gray-out menus etc.
  545.     }
  546.     else if (BitTst(&theKeys, kQKeyMap))    // Or perhaps the player hit "command - Q".
  547.     {
  548.         playing = FALSE;                    // Set flag to drop out of game loop.
  549.         quitting = TRUE;                    // Set flag to drop out of Glypha.
  550.     }
  551. }
  552.  
  553. //--------------------------------------------------------------  GetPlayerInput
  554.  
  555. // This function looks for keystrokes when a game is underway.  We don't use…
  556. // the more conventional event routines (like GetNextEvent()), because they're…
  557. // notoriously slow, allow background tasks, introduce possible INIT problems,…
  558. // and we don't have to.  Instead, we'll rely on GetKeys() (which has its own…
  559. // set of problems - but we deal with them).
  560.  
  561. void GetPlayerInput (void)
  562. {
  563.     thePlayer.flapping = FALSE;                // Assume we're not flapping.
  564.     thePlayer.walking = FALSE;                // Assume too we're not walking.
  565.  
  566.     GetKeys(theKeys);                        // Get the current keyboard keymap.
  567.     
  568.     // InputSprocket!
  569.     {
  570.         Boolean left = false;
  571.         Boolean right = false;
  572.         Boolean button = false;
  573.         
  574.         EasyJoystick(&left, &right, &button);
  575.         
  576.         if (button)    { BitSet(&theKeys, kDownArrowKeyMap); }
  577.         if (left) { BitSet(&theKeys, kLeftArrowKeyMap); }
  578.         if (right) { BitSet(&theKeys, kRightArrowKeyMap); }
  579.     }
  580.     
  581.     if (BitTst(&theKeys, kCommandKeyMap))    // See first if command key down…
  582.         DoCommandKey();                        // and handle those seperately.
  583.     else                                    // If not command key, continue.
  584.     {                                        // Look for one of the two "flap" keys.
  585.         if ((BitTst(&theKeys, kSpaceBarMap)) || (BitTst(&theKeys, kDownArrowKeyMap)))
  586.         {
  587.             if (thePlayer.mode == kIdle)    // Handle special case when player is idle.
  588.             {
  589.                 thePlayer.mode = kWalking;    // Set the player's mode now to walking.
  590.                 thePlayer.frame = 0;        // Used to note "state" of walking.
  591.             }                                // Otherwise, if player is flying or walking…
  592.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  593.             {
  594.                 if (!flapKeyDown)            // If flap key was not down last frame…
  595.                 {                            // (this is to prevent "automatic fire").
  596.                                             // Give player lift.
  597.                     thePlayer.vVel -= kFlapImpulse;
  598.                     flapKeyDown = TRUE;        // Note that the flap key is down.
  599.                                             // Play the "flap" sound.
  600.                     PlayExternalSound(kFlapSound, kFlapPriority);
  601.                                             // Set player flag to indicate flapping.
  602.                     thePlayer.flapping = TRUE;
  603.                 }
  604.             }
  605.         }
  606.         else
  607.             flapKeyDown = FALSE;            // If flap key not down, remember this.
  608.         
  609.                                             // Test now for one of three "right" keys.
  610.         if ((BitTst(&theKeys, kRightArrowKeyMap) || 
  611.                 BitTst(&theKeys, kSKeyMap) || 
  612.                 BitTst(&theKeys, kQuoteMap)) && 
  613.                 (thePlayer.hVel < kMaxHVelocity))
  614.         {
  615.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  616.             {                                // They are to begin walking (no longer idle).
  617.                 thePlayer.mode = kWalking;
  618.                 thePlayer.frame = 0;
  619.             }
  620.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  621.             {                                // If flying or walking, player moves right.
  622.                 if (!thePlayer.facingRight)    // If facing left, player does an about face.
  623.                 {
  624.                     thePlayer.facingRight = TRUE;
  625.                     if (thePlayer.clutched)
  626.                     {
  627.                         thePlayer.dest.left += 18;
  628.                         thePlayer.dest.right += 18;                        
  629.                         thePlayer.h = thePlayer.dest.left << 4;
  630.                         thePlayer.wasH = thePlayer.h;
  631.                         thePlayer.wasDest = thePlayer.dest;
  632.                     }
  633.                 }                            // Otherwise, if facing right already…
  634.                 else
  635.                 {                            // If flying, add to their horizontal velocity.
  636.                     if (thePlayer.mode == kFlying)
  637.                         thePlayer.hVel += kGlideImpulse;
  638.                     else                    // If walking, set flag to indicate a step.
  639.                         thePlayer.walking = TRUE;
  640.                 }
  641.             }
  642.         }                                    // Test now for one of three "left" keys.
  643.         else if ((BitTst(&theKeys, kLeftArrowKeyMap) || 
  644.                 BitTst(&theKeys, kAKeyMap) || 
  645.                 BitTst(&theKeys, kColonMap)) && 
  646.                 (thePlayer.hVel > -kMaxHVelocity))
  647.         {
  648.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  649.             {
  650.                 thePlayer.mode = kWalking;
  651.                 thePlayer.frame = 0;
  652.             }
  653.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  654.             {                                // If flying or walking, player moves left.
  655.                 if (thePlayer.facingRight)    // If facing right, player does an about face.
  656.                 {                            // Flag player facing left.
  657.                     thePlayer.facingRight = FALSE;
  658.                     if (thePlayer.clutched)    // Handle case where player gripped by hand.
  659.                     {                        // An about face handled a bit differently.
  660.                         thePlayer.dest.left -= 18;
  661.                         thePlayer.dest.right -= 18;
  662.                         thePlayer.h = thePlayer.dest.left << 4;
  663.                         thePlayer.wasH = thePlayer.h;
  664.                         thePlayer.wasDest = thePlayer.dest;
  665.                     }
  666.                 }
  667.                 else                        // Otherwise, player already facing left.
  668.                 {                            // So player will move left.
  669.                     if (thePlayer.mode == kFlying)
  670.                         thePlayer.hVel -= kGlideImpulse;
  671.                     else
  672.                         thePlayer.walking = TRUE;
  673.                 }
  674.             }
  675.         }
  676.     }
  677. }
  678.  
  679. //--------------------------------------------------------------  HandlePlayerIdle
  680.  
  681. // Following are a number of functions handling the player's different "modes".
  682. // This first function handles the player when in "idle" mode.  When idle, the…
  683. // player is standing on a platform - having just been "born".  This is when the…
  684. // player is in a "safe" mode - meaning no enemy can kill them.  The player remains…
  685. // in idle mode until they hit a key to flap or move or until a timer (thePlayer.frame)…
  686. // counts down to zero.
  687.  
  688. void HandlePlayerIdle (void)
  689. {
  690.     thePlayer.frame--;                // Count down the timer.
  691.     if (thePlayer.frame == 0)        // See if timer has reached zero yet.
  692.         thePlayer.mode = kWalking;    // If so, player is no longer idle.
  693.     
  694.     SetAndCheckPlayerDest();        // Keep player on platform.
  695. }
  696.  
  697. //--------------------------------------------------------------  HandlePlayerFlying
  698.  
  699. // This function handles a player in "flying" mode.  In flying mode, the player…
  700. // is alive and not standing/walking on any platform.  A plyaer remains in flying…
  701. // mode until the player dies (collides unfavorably with an enemy), is caught by…
  702. // the hand, or comes near the top of a platform (in which case they land and…
  703. // switch to walking mode).  While in flying mode, gravity pulls the player down…
  704. // while friction acts to slow the player down.
  705.  
  706. void HandlePlayerFlying (void)
  707. {    
  708.     if (thePlayer.hVel > 0)                    // If player has a positive hori. velocity…
  709.     {                                        // subtract frictional constant from velocity.
  710.         thePlayer.hVel -= kAirResistance;
  711.         if (thePlayer.hVel < 0)                // Don't let it go negative (otherwise, you…
  712.             thePlayer.hVel = 0;                // can get a "yo-yo" effect set up).
  713.     }
  714.     else if (thePlayer.hVel < 0)            // Otherwise, if horizontal velocity negative…
  715.     {                                        // add firctional constant to hori. velocity.
  716.         thePlayer.hVel += kAirResistance;
  717.         if (thePlayer.hVel > 0)
  718.             thePlayer.hVel = 0;
  719.     }
  720.     
  721.     thePlayer.vVel += kGravity;                // Add gravity to player's vertical velocity.
  722.     
  723.     if (thePlayer.vVel > kMaxVVelocity)        // Don't allow player to fall too fast.
  724.         thePlayer.vVel = kMaxVVelocity;
  725.     else if (thePlayer.vVel < -kMaxVVelocity)
  726.         thePlayer.vVel = -kMaxVVelocity;    // And don't allow player to climb too fast.
  727.     
  728.     thePlayer.h += thePlayer.hVel;            // Add velocities to players position.
  729.     thePlayer.v += thePlayer.vVel;
  730.                                             // Now we determine which graphic to use.
  731.     if (thePlayer.facingRight)                // There are the set of right-facing graphics.
  732.     {
  733.         thePlayer.srcNum = 1;                // Assume standard right-facing graphic.
  734.         if (thePlayer.vVel < -kDontFlapVel)    // Now we jump through a series of hoops…
  735.         {                                    // simply to determine whether we'll use…
  736.             if (thePlayer.flapping)            // the graphic of the player with the wings…
  737.                 thePlayer.srcNum = 0;        // up (srcNum = 0) or with the wings down…
  738.             else                            // (srcNum = 1).
  739.                 thePlayer.srcNum = 1;
  740.         }
  741.         else if (thePlayer.vVel > kDontFlapVel)
  742.         {
  743.             if (thePlayer.flapping)
  744.                 thePlayer.srcNum = 1;
  745.             else
  746.                 thePlayer.srcNum = 0;
  747.         }
  748.         else if (thePlayer.flapping)
  749.             thePlayer.srcNum = 0;
  750.     }
  751.     else                                    // If the player is facing left…
  752.     {                                        // We jump through a similar set of hoops…
  753.         thePlayer.srcNum = 2;                // this time choosing between srcNum = 2 …
  754.         if (thePlayer.vVel < -kDontFlapVel)    // and srcNum = 3.
  755.         {
  756.             if (thePlayer.flapping)
  757.                 thePlayer.srcNum = 3;
  758.             else
  759.                 thePlayer.srcNum = 2;
  760.         }
  761.         else if (thePlayer.vVel > kDontFlapVel)
  762.         {
  763.             if (thePlayer.flapping)
  764.                 thePlayer.srcNum = 2;
  765.             else
  766.                 thePlayer.srcNum = 3;
  767.         }
  768.         else if (thePlayer.flapping)
  769.             thePlayer.srcNum = 3;
  770.     }
  771.     
  772.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  773.     
  774.     CheckLavaRoofCollision();                // See if player hit top or bottom of screen.
  775.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  776.     CheckPlatformCollision();                // See if player collided with platform.
  777.     CheckTouchDownCollision();                // See if player has landed on platform.
  778. }
  779.  
  780. //--------------------------------------------------------------  HandlePlayerWalking
  781.  
  782. // This function handles a player in "walking" mode.  They remain in this mode…
  783. // until they walk off a platform's edge, flap to lift off the platform, or…
  784. // collide unfavorably with an enemy (die).  While in walking mode, we need only…
  785. // determine which frame of animation to display (if the player is taking steps)…
  786. // and check for the usual set of collisions.
  787.  
  788. void HandlePlayerWalking (void)
  789. {
  790.     short        desiredHVel;
  791.     
  792.     if (thePlayer.walking)                    // This means user is actively holding down…
  793.     {                                        // the left or right key.
  794.         if (evenFrame)                        // Now we jump through a number of hoops…
  795.         {                                    // in order to get a semi-realistic…
  796.             if (thePlayer.facingRight)        // "stepping" animation going.  We take steps…
  797.             {                                // only on "even frames".
  798.                 if (thePlayer.srcNum == 4)
  799.                     desiredHVel = 208;
  800.                 else
  801.                     desiredHVel = 128;
  802.             }
  803.             else
  804.             {
  805.                 if (thePlayer.srcNum == 7)
  806.                     desiredHVel = -208;
  807.                 else
  808.                     desiredHVel = -128;
  809.             }
  810.             
  811.             if (thePlayer.hVel < desiredHVel)
  812.             {
  813.                 thePlayer.hVel += 80;        // Move player right.
  814.                 if (thePlayer.hVel > desiredHVel)
  815.                 {                            // This is the case where player is walking.
  816.                     thePlayer.hVel = desiredHVel;
  817.                     PlayExternalSound(kWalkSound, kWalkPriority);
  818.                 }
  819.                 else
  820.                 {                        // In this case, player is skidding.
  821.                     PlayExternalSound(kScreechSound, kScreechPriority);
  822.                     StartPixelShatter((thePlayer.dest.left + thePlayer.dest.right) / 2,
  823.                                         thePlayer.dest.bottom, thePlayer.hVel, thePlayer.vVel, kShatterPlatformScrape);
  824.             
  825.                 }
  826.             }
  827.             else
  828.             {
  829.                 thePlayer.hVel -= 80;        // Move player to the left.
  830.                 if (thePlayer.hVel < desiredHVel)
  831.                 {                            // Player is stepping to left.
  832.                     thePlayer.hVel = desiredHVel;
  833.                     PlayExternalSound(kWalkSound, kWalkPriority);
  834.                 }
  835.                 else
  836.                 {                        // Player is skidding to a stop.
  837.                     PlayExternalSound(kScreechSound, kScreechPriority);
  838.                     StartPixelShatter((thePlayer.dest.left + thePlayer.dest.right) / 2,
  839.                                     thePlayer.dest.bottom, thePlayer.hVel, thePlayer.vVel, kShatterPlatformScrape);
  840.  
  841.                 }
  842.             }
  843.         }
  844.     }
  845.     else                                    // If user is not actively holding down the…
  846.     {                                        // left or right key, bring player to a stop.
  847.         thePlayer.hVel -= thePlayer.hVel / 4;
  848.         if ((thePlayer.hVel < 4) && (thePlayer.hVel > -4))
  849.             thePlayer.hVel = 0;                // If close to zero (within 4), stop player.
  850.         else                                // Othewrwise, play the skidding sound.
  851.             PlayExternalSound(kScreechSound, kScreechPriority);
  852.     }
  853.     
  854.     if (thePlayer.vVel > kMaxVVelocity)        // Keep player from moving too quickly…
  855.         thePlayer.vVel = kMaxVVelocity;        // left or right.
  856.     else if (thePlayer.vVel < -kMaxVVelocity)
  857.         thePlayer.vVel = -kMaxVVelocity;
  858.     
  859.     thePlayer.h += thePlayer.hVel;            // Move player horizontally and vertically…
  860.     thePlayer.v += thePlayer.vVel;            // by the corresponding velocity.
  861.     
  862.     if (thePlayer.walking)                    // "If player holding down left or right keys…".
  863.     {
  864.         if (evenFrame)                        // Here's where we toggle between the two…
  865.         {                                    // frames of "stepping" animation.
  866.             if (thePlayer.facingRight)
  867.                 thePlayer.srcNum = 9 - thePlayer.srcNum;
  868.             else
  869.                 thePlayer.srcNum = 13 - thePlayer.srcNum;
  870.         }
  871.     }
  872.     else                                    // If the player not holding down keys…
  873.     {                                        // draw the player just standing there.
  874.         if (thePlayer.facingRight)
  875.             thePlayer.srcNum = 5;
  876.         else
  877.             thePlayer.srcNum = 6;
  878.     }
  879.     
  880.     SetAndCheckPlayerDest();                // Check for wrap-around and all that.
  881.     
  882.     CheckTouchDownCollision();                // See if player still on platform.
  883.     KeepPlayerOnPlatform();                    // Don't let player "sink through" ledge.
  884.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  885. }
  886.  
  887. //--------------------------------------------------------------  HandlePlayerSinking
  888.  
  889. // When the player is in "sinking" mode, they are on a one-way ticket to death.
  890. // The player is sinking into the lava.  We put the player into this mode (rather…
  891. // than kill them outright) so that we can have a number of frames of them slowly…
  892. // slipping beneath the surface of the lava.  When the get below the surface of…
  893. // the lava, they will be officially "killed" and a new player will be "born",
  894.  
  895. void HandlePlayerSinking (void)
  896. {
  897.     thePlayer.hVel = 0;                        // Don't allow horizontal motion.
  898.     thePlayer.vVel = 16;                    // They will sink at this constant rate.
  899.     if (thePlayer.dest.top > kLavaHeight)    // See if they slipped below the surface.
  900.         OffAMortal();                        // If they did, kill 'em.
  901.     
  902.     thePlayer.v += thePlayer.vVel;            // Otherwise, move them down a notch.
  903.     
  904.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  905. }
  906.  
  907. //--------------------------------------------------------------  HandlePlayerFalling
  908.  
  909. // "Falling" refers to a player who is dead already but is still careening…
  910. // down the screen as a skeleton.  If (when) the player lands on a ledge they…
  911. // will turn into a pile of bones for a short duration.  If instead they fall…
  912. // into the lava, they'll sink.  In any event, it is then that they are…
  913. // officially pronounced dead and a new player is born.
  914.  
  915. void HandlePlayerFalling (void)
  916. {
  917.     if (thePlayer.hVel > 0)                // Handle horizontal air resistance.
  918.     {
  919.         thePlayer.hVel -= kAirResistance;
  920.         if (thePlayer.hVel < 0)
  921.             thePlayer.hVel = 0;
  922.     }
  923.     else if (thePlayer.hVel < 0)
  924.     {
  925.         thePlayer.hVel += kAirResistance;
  926.         if (thePlayer.hVel > 0)
  927.             thePlayer.hVel = 0;
  928.     }
  929.     
  930.     thePlayer.vVel += kGravity;            // Add in effect of gravity.
  931.     
  932.     if (thePlayer.vVel > kMaxVVelocity)    // Keep player from falling too fast.
  933.         thePlayer.vVel = kMaxVVelocity;
  934.     else if (thePlayer.vVel < -kMaxVVelocity)
  935.         thePlayer.vVel = -kMaxVVelocity;
  936.     
  937.     thePlayer.h += thePlayer.hVel;        // Move player's x and y (h and v)…
  938.     thePlayer.v += thePlayer.vVel;        // by amount of velocity in each direction.
  939.     
  940.     SetAndCheckPlayerDest();            // Check for wrap-around, etc.
  941.     
  942.     CheckLavaRoofCollision();            // See if they hit roof or lava.
  943.     CheckPlatformCollision();            // See if they crashed to a ledge.
  944. }
  945.  
  946. //--------------------------------------------------------------  HandlePlayerBones
  947.  
  948. // This is when the player is just a static pile of bones on a platform.  They…
  949. // have been killed by an enemy and now are waiting to slip away so that a new…
  950. // player can be born.
  951.  
  952. void HandlePlayerBones (void)
  953. {
  954.     if (evenFrame)                    // To slow it down a bit, action only occurs…
  955.     {                                // on the even frames.
  956.         thePlayer.frame--;            // Drop the counter down by one.
  957.         if (thePlayer.frame == 0)    // When counter reaches zero, player officially dies.
  958.             OffAMortal();
  959.         else                        // Otherwise, player's bones are sinking.
  960.             thePlayer.dest.top = thePlayer.dest.bottom - thePlayer.frame;
  961.     }
  962. }
  963.  
  964. //--------------------------------------------------------------  MovePlayer
  965.  
  966. // This function is the sort of "master movement" function.  It looks…
  967. // at what mode a player is in and calls the appropriate function from…
  968. // above.  Arcade games (at least this one) tend to be very "modal" in…
  969. // this way.  It's the actions of the user and the enemies in the game…
  970. // that cause the player's mode to move from one state to another.
  971.  
  972. void MovePlayer (void)
  973. {
  974.     switch (thePlayer.mode)        // Check the "mode" the player is in.
  975.     {
  976.         case kIdle:                // Invulnerable - standing there - just born.
  977.         HandlePlayerIdle();
  978.         break;
  979.         
  980.         case kFlying:            // Flapping, floating, airborne.
  981.         HandlePlayerFlying();
  982.         break;
  983.         
  984.         case kWalking:            // On terra firma.  Standing or walking on ledge.
  985.         HandlePlayerWalking();
  986.         break;
  987.         
  988.         case kSinking:            // Trapped in the lava - going down.
  989.         HandlePlayerSinking();
  990.         break;
  991.         
  992.         case kFalling:            // Dead - a skeleton falling to earth.
  993.         HandlePlayerFalling();
  994.         break;
  995.         
  996.         case kBones:            // Dead - a static pile of bones on a ledge.
  997.         HandlePlayerBones();
  998.         break;
  999.     }
  1000. }
  1001.  
  1002. //--------------------------------------------------------------  CheckTouchDownCollision
  1003.  
  1004. // This function determines whether or not the player is landed on a ledge.
  1005. // It does this by doing a rectangle collision between the player's bounding…
  1006. // rectangle and an imaginary rectangle enclosing an area above the ledges.
  1007. // I call these imaginary rectangles "touchDownRects[]".  The trick was that…
  1008. // you don't want the player to have to "hit" the top of a ledge in order to…
  1009. // land on it - there is an arbitrary distance above a ledge where, if the player…
  1010. // is within this area, the legs ought to come out and the player flagged as…
  1011. // walking.  As well, this same function is used for a walking player to see…
  1012. // if they are still on the ledge (they may walk off the edge).
  1013.  
  1014. void CheckTouchDownCollision (void)
  1015. {
  1016.     Rect        testRect, whoCares;
  1017.     short        i, offset;
  1018.     Boolean        sected;
  1019.     
  1020.     sected = FALSE;                                // Assume not on ledge.
  1021.     for (i = 0; i < numLedges; i++)                // Go through all ledges.
  1022.     {
  1023.         testRect = touchDownRects[i];            // Here's the imaginary rect.
  1024.         if (thePlayer.mode == kWalking)            // We need an offset if player walking…
  1025.             OffsetRect(&testRect, 0, 11);        // since the player graphic is taller.
  1026.         
  1027.         if (SectRect(&thePlayer.dest, &testRect, &whoCares))
  1028.         {                                        // Does the player's rect intersect?
  1029.             if (thePlayer.mode == kFlying)        // Okay, it does, is the player airborne?
  1030.             {
  1031.                 StartPixelShatter((thePlayer.dest.right+thePlayer.dest.left)/2,
  1032.                                     thePlayer.dest.bottom,
  1033.                                     thePlayer.hVel,
  1034.                                     thePlayer.vVel, kShatterPlatformScrape);
  1035.  
  1036.                 thePlayer.mode = kWalking;        // Put player into walking mode.
  1037.                 if (thePlayer.facingRight)        // Assign correct graphic for player.
  1038.                     thePlayer.srcNum = 5;
  1039.                 else
  1040.                     thePlayer.srcNum = 6;
  1041.                 if (thePlayer.vVel > 0)            // Stop player from falling further.
  1042.                     thePlayer.vVel = 0;
  1043.                 thePlayer.dest.bottom += 11;    // "Grow" player's bounding rect.
  1044.                 thePlayer.wasDest.bottom += 11;
  1045.                                                 // Move player so standing on top of ledge.
  1046.                 offset = thePlayer.dest.bottom - testRect.bottom - 1;
  1047.                 thePlayer.dest.bottom -= offset;
  1048.                 thePlayer.dest.top -= offset;
  1049.                 thePlayer.v = thePlayer.dest.top << 4;
  1050.                                                 // Play brief collision sound.
  1051.                 PlayExternalSound(kGrateSound, kGratePriority);
  1052.             }
  1053.             
  1054.             sected = TRUE;                        // Make note that we've landed.
  1055.         }
  1056.     }
  1057.     
  1058.     if (!sected)                                // Now, if we didn't collide…
  1059.     {                                            // were we walking?
  1060.         if (thePlayer.mode == kWalking)            // Did we walk off the ledge?
  1061.         {
  1062.             thePlayer.mode = kFlying;            // Set player to flying mode.
  1063.             thePlayer.dest.bottom -= 11;        // Resize player's bounding rect.
  1064.             thePlayer.wasDest.bottom -= 11;
  1065.         }
  1066.     }
  1067. }
  1068.  
  1069. //--------------------------------------------------------------  CheckPlatformCollision
  1070.  
  1071. // Unlike the above function, this one tests the player's bounding rect against…
  1072. // the bounding rect of each ledge (not an imaginary rect above the ledge).  This…
  1073. // function is primarily for (then) collisions off the bottom and sides of the…
  1074. // ledges.  In this way, the ledges are "solid" - not able to be passed through.
  1075.  
  1076. void CheckPlatformCollision (void)
  1077. {
  1078.     Rect        hRect, vRect, whoCares;
  1079.     short        i, offset;
  1080.     
  1081.     for (i = 0; i < numLedges; i++)                    // Walk through all ledges.
  1082.     {                                                // Test rectangle overlap.
  1083.         if (SectRect(&thePlayer.dest, &platformRects[i], &whoCares))
  1084.         {                                            // If player intersecting ledge…
  1085.             hRect.left = thePlayer.dest.left;        // Create our special test rect.
  1086.             hRect.right = thePlayer.dest.right;
  1087.             hRect.top = thePlayer.wasDest.top;
  1088.             hRect.bottom = thePlayer.wasDest.bottom;
  1089.                                                     // Determine if the player hit the…
  1090.                                                     // top/bottom of the ledge or the…
  1091.                                                     // sides of the ledge.
  1092.             if (SectRect(&hRect, &platformRects[i], &whoCares))
  1093.             {                                        // We're fairly sure the player hit…
  1094.                                                     // the left or right edge of ledge.
  1095.                                                     
  1096.                 PlayExternalSound(kGrateSound, kGratePriority);
  1097.                 
  1098.                 StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1099.                                     thePlayer.dest.bottom,
  1100.                                     thePlayer.hVel,
  1101.                                     thePlayer.vVel, kShatterPlatformHit);
  1102.                                     
  1103.                 if (thePlayer.h > thePlayer.wasH)    // If player was heading right…
  1104.                 {                                    // player will bounce to left.
  1105.                     offset = thePlayer.dest.right - platformRects[i].left;
  1106.                     thePlayer.dest.left -= offset;
  1107.                     thePlayer.dest.right -= offset;
  1108.                     thePlayer.h = thePlayer.dest.left << 4;
  1109.                     if (thePlayer.hVel > 0)            // We bounce back with 1/2 our vel.
  1110.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  1111.                     else
  1112.                         thePlayer.hVel = thePlayer.hVel >> 1;
  1113.                 }                                    // Else if player was heading left…
  1114.                 else if (thePlayer.h < thePlayer.wasH)
  1115.                 {                                    // player will bounce right.
  1116.                     offset = platformRects[i].right - thePlayer.dest.left;
  1117.                     thePlayer.dest.left += offset;
  1118.                     thePlayer.dest.right += offset;
  1119.                     thePlayer.h = thePlayer.dest.left << 4;
  1120.                     if (thePlayer.hVel < 0)            // We bounce back with 1/2 our vel.
  1121.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  1122.                     else
  1123.                         thePlayer.hVel = thePlayer.hVel >> 1;
  1124.                 }                                    // Play impact sound.
  1125.  
  1126.             }
  1127.             else                                    // It doesn't look like we hit the…
  1128.             {                                        // the left or right edge of ledge.
  1129.                 vRect.left = thePlayer.wasDest.left;
  1130.                 vRect.right = thePlayer.wasDest.right;
  1131.                 vRect.top = thePlayer.dest.top;
  1132.                 vRect.bottom = thePlayer.dest.bottom;
  1133.                                                     // So we'll test top/bottom collision.
  1134.                 if (SectRect(&vRect, &platformRects[i], &whoCares))
  1135.                 {                                    // We've decided we've hit top/bottom.
  1136.                     if (thePlayer.wasV < thePlayer.v)
  1137.                     {                                // If we were heading down (hit top)…
  1138.                                                     // keep player on top of ledge.
  1139.                         offset = thePlayer.dest.bottom - platformRects[i].top;
  1140.                         thePlayer.dest.top -= offset;
  1141.                         thePlayer.dest.bottom -= offset;
  1142.                         thePlayer.v = thePlayer.dest.top << 4;
  1143.                                                     // Play collision sound.
  1144.                         if (thePlayer.vVel > kDontFlapVel)
  1145.                         {                
  1146.                             StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1147.                                     thePlayer.dest.bottom,
  1148.                                     thePlayer.hVel,
  1149.                                     thePlayer.vVel, kShatterPlatformScrape);
  1150.  
  1151.                             PlayExternalSound(kGrateSound, kGratePriority);
  1152.                         }
  1153.                                                     // If we were falling bones (dead)…
  1154.                         if (thePlayer.mode == kFalling)
  1155.                         {                            // we'll bounce.
  1156.                             if ((thePlayer.dest.right - 16) > platformRects[i].right)                            {
  1157.                                 thePlayer.hVel = 16;
  1158.                                 if (thePlayer.vVel > 0)
  1159.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1160.                                 else
  1161.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1162.                             }
  1163.                             else if ((thePlayer.dest.left + 16) < platformRects[i].left)
  1164.                             {
  1165.                                 thePlayer.hVel = -16;
  1166.                                 if (thePlayer.vVel > 0)
  1167.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1168.                                 else
  1169.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1170.                             }
  1171.                             else                    // If we were nearly stopped…
  1172.                             {                        // turn into pile of bones.
  1173.                                 PlayExternalSound(kBoom1Sound, kBoom1Priority);
  1174.                                 thePlayer.vVel = 0;
  1175.                                 thePlayer.mode = kBones;
  1176.                                 thePlayer.frame = 22;
  1177.                                 thePlayer.dest.top = thePlayer.dest.bottom - 22;
  1178.                                 thePlayer.v = thePlayer.dest.top << 4;
  1179.                                 thePlayer.srcNum = 10;
  1180.                             }
  1181.                         }
  1182.                         else                        // Okay, if we weren't falling bones…
  1183.                         {                            // bounce the player (-1/2 vel.).
  1184.                             if (thePlayer.vVel > 0)
  1185.                                 thePlayer.vVel = -(thePlayer.vVel >> 1);
  1186.                             else
  1187.                                 thePlayer.vVel = thePlayer.vVel >> 1;
  1188.                         }
  1189.                     }                                // If the player was instead moving up…
  1190.                     else if (thePlayer.wasV > thePlayer.v)
  1191.                     {                                // the player likely hit the bottom of…
  1192.                                                     // the ledge.  Keep player below ledge.
  1193.                                                     
  1194.                         StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1195.                                             thePlayer.dest.bottom,
  1196.                                             thePlayer.hVel,
  1197.                                             thePlayer.vVel, kShatterPlatformHit);
  1198.                         
  1199.                         offset = platformRects[i].bottom - thePlayer.dest.top;
  1200.                         thePlayer.dest.top += offset;
  1201.                         thePlayer.dest.bottom += offset;
  1202.                         thePlayer.v = thePlayer.dest.top << 4;
  1203.                                                     // Play collision sound.
  1204.                         PlayExternalSound(kGrateSound, kGratePriority);
  1205.                         if (thePlayer.vVel < 0)        // Bounce player down (-1/2 vel.).
  1206.                             thePlayer.vVel = -(thePlayer.vVel >> 1);
  1207.                         else
  1208.                             thePlayer.vVel = thePlayer.vVel >> 1;
  1209.                     }
  1210.                 }
  1211.             }
  1212.         }
  1213.     }
  1214. }
  1215.  
  1216. //--------------------------------------------------------------  KeepPlayerOnPlatform
  1217.  
  1218. // This is an alignment function.  It is called only if the player is standing or…
  1219. // walking on a ledge.  It is designed to keep the player's mount's (bird's)…
  1220. // feet firmly planted on the ledge.  Consider that, with the addition of gravity…
  1221. // to a player's downward velocity, there is a problem where the player can appear…
  1222. // to slowly sink down through the ledge.  There may be any number of methods you…
  1223. // might want to try to prevent this from becoming a problem in the first place, …
  1224. // but my experience has been that all the methods I've tried have flaws - correcting…
  1225. // for those flaws points out other flaws and you start getting a messy sort of…
  1226. // patchwork.  Should you ever get it to work, the mess that is your function has come…
  1227. // to resemble the Knot of ????.
  1228.  
  1229. void KeepPlayerOnPlatform (void)
  1230. {
  1231.     Rect        whoCares;
  1232.     short        i, offset;
  1233.     
  1234.     for (i = 0; i < numLedges; i++)        // For each ledge for this wave…
  1235.     {                                    // test for a collision.
  1236.         if ((SectRect(&thePlayer.dest, &platformRects[i], &whoCares)) && (thePlayer.vVel > 0))
  1237.         {                                // If collided (player sinking), force…
  1238.                                         // player to top of ledge.
  1239.             offset = thePlayer.dest.bottom - platformRects[i].top - 1;
  1240.             thePlayer.dest.top -= offset;
  1241.             thePlayer.dest.bottom -= offset;
  1242.             thePlayer.v = thePlayer.dest.top * 16;
  1243.         }
  1244.     }
  1245.     
  1246.     if (thePlayer.vVel > 0)                // Set player's vertical velocity to zero.
  1247.         thePlayer.vVel = 0;
  1248. }
  1249.  
  1250. //--------------------------------------------------------------  CheckLavaRoofCollision
  1251.  
  1252. // This is a simple high/low test to see if the player has either bounced off…
  1253. // the roof of the "arena" or dipped down into the lava below.
  1254.  
  1255. void CheckLavaRoofCollision (void)
  1256. {
  1257.     short        offset;
  1258.     
  1259.     if (thePlayer.dest.bottom > kLavaHeight)    // See if player in lava.
  1260.     {
  1261.         if (thePlayer.mode == kFalling)            // If falling (dead), "Splash!"
  1262.             PlayExternalSound(kSplashSound, kSplashPriority);
  1263.         else                                    // If flying (alive), "Yeow!"
  1264.             PlayExternalSound(kBirdSound, kBirdPriority);
  1265.     
  1266.         {
  1267.             short left = thePlayer.dest.left;
  1268.             short right = thePlayer.dest.right;
  1269.             short bottom = thePlayer.dest.bottom;
  1270.             short delta = (right - left);
  1271.             short temp = left * 32;
  1272.             short splashItr;
  1273.             
  1274.             for(splashItr = 0; splashItr < 32; splashItr++)
  1275.             {
  1276.                 StartPixelShatter(temp / 32, bottom, (thePlayer.hVel * 0.75), -(thePlayer.vVel * 0.75), kShatterLavaSplash);
  1277.                 temp += delta;
  1278.             }
  1279.         }
  1280.  
  1281.         thePlayer.mode = kSinking;                // Irregardless, player is now sinking.
  1282.     }
  1283.     else if (thePlayer.dest.top < kRoofHeight)    // See if player hit roof.
  1284.     {                                            // Move player to below roof.
  1285.         offset = kRoofHeight - thePlayer.dest.top;
  1286.         thePlayer.dest.top += offset;
  1287.         thePlayer.dest.bottom += offset;
  1288.         thePlayer.v = thePlayer.dest.top * 16;
  1289.                                                 // Play collision sound.
  1290.         PlayExternalSound(kGrateSound, kGratePriority);
  1291.         thePlayer.vVel = thePlayer.vVel / -2;    // Rebound player (-1/2 vel.).
  1292.     }
  1293. }
  1294.  
  1295. //--------------------------------------------------------------  SetAndCheckPlayerDest
  1296.  
  1297. // This function keeps our player's screen coordinates and "scaled" coordinates…
  1298. // in agreement.  As well, it checks for wrap-around and handles it.
  1299.  
  1300. void SetAndCheckPlayerDest (void)
  1301. {
  1302.     short        wasTall, wasWide;
  1303.                                         // Remember width and height of player.
  1304.     wasTall = thePlayer.dest.bottom - thePlayer.dest.top;
  1305.     wasWide = thePlayer.dest.right - thePlayer.dest.left;
  1306.                                         // Convert scaled coords to screen coords.
  1307.     thePlayer.dest.left = thePlayer.h >> 4;
  1308.     thePlayer.dest.right = thePlayer.dest.left + wasWide;
  1309.     thePlayer.dest.top = thePlayer.v >> 4;
  1310.     thePlayer.dest.bottom = thePlayer.dest.top + wasTall;
  1311.     
  1312.     if (thePlayer.dest.left > 640)        // Has player left right side of arena?
  1313.     {                                    // Wrap player back to left side of screen.
  1314.         OffsetRect(&thePlayer.dest, -640, 0);
  1315.         thePlayer.h = thePlayer.dest.left << 4;
  1316.         OffsetRect(&thePlayer.wasDest, -640, 0);
  1317.     }
  1318.     else if (thePlayer.dest.right < 0)    // Else, has player left left side of screen?
  1319.     {                                    // Wrap player around to right side of screen.
  1320.         OffsetRect(&thePlayer.dest, 640, 0);
  1321.         thePlayer.h = thePlayer.dest.left << 4;
  1322.         OffsetRect(&thePlayer.wasDest, 640, 0);
  1323.     }
  1324. }
  1325.  
  1326. //--------------------------------------------------------------  HandleLightning
  1327.  
  1328. // Lightning is handled here.  Obelisks are flashed, lightning is generated, …
  1329. // lighting strikes, and the lightning counter decremented.  This is pretty…
  1330. // nice - we can just set "lightningCount" to a non-zero number and this…
  1331. // function will strike lightning every fram until the counter returns to zero.
  1332.  
  1333. void HandleLightning (void)
  1334. {
  1335.     Boolean doLightning = false;
  1336.     short hLoc;
  1337.     short vLoc;
  1338.     
  1339.     if (thePlayer.electrical > 0)
  1340.     {
  1341.         lightningCount = 0;
  1342.         
  1343.         hLoc = (thePlayer.dest.left + thePlayer.dest.right) / 2;
  1344.         vLoc = (thePlayer.dest.top + thePlayer.dest.bottom) / 2;
  1345.         thePlayer.electrical--;
  1346.         
  1347.         doLightning = true;
  1348.     }
  1349.     else if (lightningCount > 0)                        // Is lightning to strik this frame?
  1350.     {    
  1351.         lightningCount--;
  1352.         hLoc = lightH;
  1353.         vLoc = lightV;
  1354.         
  1355.         doLightning = true;
  1356.     }
  1357.     
  1358.     if (doLightning)
  1359.     {
  1360.         GenerateLightning(hLoc, vLoc);
  1361.         StrikeLightningWork();
  1362.     }
  1363. }
  1364.  
  1365. //--------------------------------------------------------------  HandleCountDownTimer
  1366.  
  1367. // This is a pretty boring function.  It is here so that when one level ends,…
  1368. // the next one does begin immediately.  It gives the player a few seconds of…
  1369. // breathing time.  Essentially, to engage it, we need merely set "countDownTimer"…
  1370. // to a positive number.  Each frame the counter gets decremented.  When it…
  1371. // reaches zero, the level is advanced to the next wave.
  1372.  
  1373. void HandleCountDownTimer (void)
  1374. {
  1375.     if (countDownTimer == 0)        // If already zero, do nothing.
  1376.         return;
  1377.     else                            // Otherwise, if greater than zero…
  1378.     {
  1379.         countDownTimer--;            // decrememnt counter.
  1380.         if (countDownTimer == 0)    // Did it just hit zero?
  1381.         {
  1382.             countDownTimer = 0;        // Well, just to be sure (dumb line of code).
  1383.             levelOn++;                // Increment the level (wave) we're on.
  1384.             UpdateLevelNumbers();    // Display new level on screen.
  1385.             SetUpLevel();            // Set up the platforms.
  1386.             GenerateEnemies();        // Ready nemesis.
  1387.         }
  1388.     }
  1389. }
  1390.  
  1391. //--------------------------------------------------------------  PlayGame
  1392.  
  1393. // Here is the "core" of the "game loop".  When a player has elected to…
  1394. // begin a game, Glypha falls into this function and remains in a loop…
  1395. // herein until the player either quits, or loses their last "bird".
  1396. // Each pass through the main loop below constitutes one "frame" of the game.
  1397.  
  1398. void PlayGame (void)
  1399. {
  1400.     Point        offsetPt;
  1401.     UnsignedWide thisTime;
  1402.     
  1403.     offsetPt.h = 0;                                // Set up ShieldCursor() point.
  1404.     offsetPt.v = 20;
  1405.  
  1406.  
  1407.     oldFrameRate = 0;
  1408.     Microseconds(&frameTime);
  1409.     
  1410.     frameTime.lo += 1000000;
  1411.         
  1412.     do                                            // Main game loop!!!!
  1413.     {
  1414.         Microseconds(&thisTime);
  1415.         
  1416.         if ((thisTime.hi > frameTime.hi) || 
  1417.             ((thisTime.hi == frameTime.hi) && (thisTime.lo > frameTime.lo)))
  1418.         {
  1419.             UpdateLevelNumbers();                    // update the level, using for frame rate
  1420.             oldFrameRate = currentFrameRate;
  1421.             currentFrameRate = 0;
  1422.             Microseconds(&frameTime);
  1423.             frameTime.lo += 1000000;
  1424.         }
  1425.         else
  1426.         {
  1427.             currentFrameRate++;
  1428.         }
  1429.         
  1430.         MovePlayer();                            // Move the player's bird.
  1431.         MoveEnemies();                            // Move all sphinx enemies.
  1432.         HandleHand();                            // Handle the mummy hand (may do nothing).
  1433.         HandleEye();                            // Handle eye (probably will do nothing).
  1434.         HandlePixelShatter();
  1435.         HandleScoreFloaters();
  1436.         
  1437.         HandleLava();
  1438.         
  1439.         DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
  1440.  
  1441.         
  1442.         DumpBackToWorkMap();                    // clear the screen
  1443.         DrawFrame();                            // Draw the whole scene for this frame.
  1444.         HandleLightning();
  1445.  
  1446.         DSpContext_SwapBuffers( gTheContext, NULL, NULL );
  1447.  
  1448.         evenFrame = !evenFrame;                    // Toggle "evenFrame" variable.
  1449.         
  1450.         GetPlayerInput();                        // Get the player's input (keystrokes).
  1451.         HandleCountDownTimer();                    // Handle countdown (may do nothing).
  1452.     }
  1453.     while ((playing) && (!pausing));            // Stay in loop until dead, paused or quit.
  1454.     
  1455.     if ((!playing) && (!quitting))                // If the player died!
  1456.     {                                            // Then play some sweet music.
  1457.         PlayExternalSound(kMusicSound, kMusicPriority);
  1458.         CheckHighScore();                        // And see if they're on the high scores.
  1459.     }
  1460.     
  1461.     MenusReflectMode();                            // Set the menus grayed-out state correctly.
  1462.     FlushEvents(everyEvent, 0);                    // Flush any events in the queue.
  1463. }
  1464.  
  1465. //--------------------------------------------------------------  CheckHighScore
  1466.  
  1467. // This function handles testing to see if the player's score is in the …
  1468. // high scores.  If that is the case, the function prompts the user for…
  1469. // a name to enter, and sorts and stores off the new score list.
  1470.  
  1471. void CheckHighScore (void)
  1472. {
  1473.     #define        kHighNameDialogID    130
  1474.     Str255        placeStr, tempStr;
  1475.     DialogPtr    theDial;
  1476.     short        i, item;
  1477.     Boolean        leaving;
  1478.     
  1479.     if (theScore > thePrefs.highScores[9])        // To see if on high scores, we need…
  1480.     {                                            // merely see if the last guy is beat out.
  1481.         openTheScores = TRUE;                    // Will automatically bring up high scores.
  1482.                                                 // Play some congratulatory music.
  1483.         PlayExternalSound(kBonusSound, kMusicPriority - 1);
  1484.         i = 8;                                    // Find where new score fits in list.
  1485.         while ((theScore > thePrefs.highScores[i]) && (i >= 0))
  1486.         {                                        // We'll bump everyone down as we look.
  1487.             thePrefs.highScores[i + 1] = thePrefs.highScores[i];
  1488.             thePrefs.highLevel[i + 1] = thePrefs.highLevel[i];
  1489.             PasStringCopy(thePrefs.highNames[i], thePrefs.highNames[i + 1]);
  1490.             i--;
  1491.         }
  1492.         
  1493.         i++;                                    // i is our place in list (zero based).
  1494.         thePrefs.highScores[i] = theScore;        // Pop the new score in place.
  1495.         thePrefs.highLevel[i] = levelOn + 1;    // Drop in the new highest level.
  1496.         
  1497.         NumToString((long)i + 1L, placeStr);    // Convert place to a string to display…
  1498.         ParamText(placeStr, "\p", "\p", "\p");    // in the dialog (via ParamText()).
  1499.         
  1500.         InitCursor();                            // Show cursor.
  1501.         CenterDialog(kHighNameDialogID);        // Center the dialog and then bring it up.
  1502.         theDial = GetNewDialog(kHighNameDialogID, 0L, kPutInFront);
  1503.         SetPort((GrafPtr)theDial);
  1504.         ShowWindow((GrafPtr)theDial);            // Make dialog visible.
  1505.         DrawDefaultButton(theDial);                // Draw outline around "Okay" button.
  1506.         FlushEvents(everyEvent, 0);                // Flush any events queued up.
  1507.                                                 // Put a default name in text edit box.
  1508.         SetDialogString(theDial, 2, thePrefs.highName);
  1509.         SelectDialogItemText(theDial, 2, 0, 1024);        // Select the whole text edit string.
  1510.         leaving = FALSE;                        // Flag for noting when player hit "Okay".
  1511.         
  1512.         while (!leaving)                        // Simple modal dialog loop.
  1513.         {
  1514.             ModalDialog(0L, &item);                // Use standard filtering.
  1515.             
  1516.             if (item == 1)                        // If player hit the "Okay" button…
  1517.             {                                    // Get the name entered in text edit box.
  1518.                 GetDialogString(theDial, 2, tempStr);
  1519.                                                 // Copy the name into high score list.
  1520.                 PasStringCopyNum(tempStr, thePrefs.highNames[i], 15);
  1521.                 PasStringCopy(thePrefs.highNames[i], thePrefs.highName);
  1522.                 leaving = TRUE;                    // We're gone!
  1523.             }
  1524.         }
  1525.         
  1526.         DisposeDialog(theDial);                    // Clean up.
  1527.     }
  1528.     else                                        // But if player didn't get on high scores…
  1529.         openTheScores = FALSE;                    // no need to rub their face in it.
  1530. }
  1531.  
  1532.  
  1533. void HandlePixelShatter(void)
  1534. {
  1535.     int i = 0;
  1536.     
  1537.     while(i < numPixelShatter)
  1538.     {
  1539.         if (thePixelShatter[i].duration == 0)
  1540.         {
  1541.             thePixelShatter[i] = thePixelShatter[numPixelShatter];
  1542.             numPixelShatter--;
  1543.             continue;
  1544.         }
  1545.         
  1546.         thePixelShatter[i].duration--;
  1547.         thePixelShatter[i].h += thePixelShatter[i].hVel;
  1548.         thePixelShatter[i].v += thePixelShatter[i].vVel;    
  1549.         thePixelShatter[i].hVel += RandomInt(5) - 2;
  1550.         thePixelShatter[i].vVel += kGravity + RandomInt(5) - 2;
  1551.         
  1552.         if (    (thePixelShatter[i].type == kShatterLavaBubble) ||
  1553.                 (thePixelShatter[i].type == kShatterLavaSplash) ||
  1554.                 (thePixelShatter[i].type == kShatterLavaBurst))
  1555.         {
  1556.             thePixelShatter[i].color += RandomInt(3) - 1;
  1557.             if (thePixelShatter[i].color > 220)
  1558.             {
  1559.                 thePixelShatter[i].color = 220; 
  1560.             }
  1561.             else if (thePixelShatter[i].color < 216)
  1562.             {
  1563.                 thePixelShatter[i].color = 216;
  1564.             }
  1565.         }
  1566.         else if ((thePixelShatter[i].type == kShatterPlatformLightning))
  1567.         {
  1568.             if (thePixelShatter[i].duration > 18)
  1569.             {
  1570.                 thePixelShatter[i].color = 5;
  1571.             }
  1572.             else if (thePixelShatter[i].duration > 12)
  1573.             {
  1574.                 thePixelShatter[i].color = 47;
  1575.             }
  1576.             else if (thePixelShatter[i].duration > 6)
  1577.             {
  1578.                 thePixelShatter[i].color = 89;
  1579.             }
  1580.             else
  1581.             {
  1582.                 thePixelShatter[i].color = 255;
  1583.             }
  1584.         }
  1585.         else if (    (thePixelShatter[i].type == kShatterObeliskTip) ||
  1586.                      (thePixelShatter[i].type == kShatterLightningDust) ||
  1587.                      (thePixelShatter[i].type == kShatterPlayerDeath) ||
  1588.                      (thePixelShatter[i].type == kShatterEnemyDeath) ||
  1589.                      (thePixelShatter[i].type == kShatterPlayerEnemyScrape))
  1590.         {
  1591.             // white 0
  1592.             // yellow 1,2,3,4,5
  1593.             // orange 11, 17, 23, 29
  1594.             // red 35, ... 216, 218, 220, 222, 223
  1595.             // black 255
  1596.             
  1597.             unsigned char blackBodyTable[17] = {    0,                    // white
  1598.                                                 1,2,3,4,5,            // yellow (5)
  1599.                                                 11,17,23,29,        // orange (4)
  1600.                                                 35, 216, 218, 220, 222, 223, // red (6)
  1601.                                                 255                    // black
  1602.                                             };
  1603.                                             
  1604.             if (thePixelShatter[i].duration >= 17)
  1605.             {
  1606.                 thePixelShatter[i].color = 0;
  1607.             }
  1608.             else
  1609.             {
  1610.                 thePixelShatter[i].color = blackBodyTable[thePixelShatter[i].duration];
  1611.             }
  1612.         }
  1613.         else
  1614.         {
  1615.             if (thePixelShatter[i].duration > 23)
  1616.             {
  1617.                 thePixelShatter[i].color = 0;
  1618.             }
  1619.             else
  1620.             {
  1621.                 thePixelShatter[i].color = 255 - (thePixelShatter[i].duration / 2);
  1622.             }
  1623.         }
  1624.         
  1625.         thePixelShatter[i].h %= (640 * 16) ;
  1626.         
  1627.         if ((thePixelShatter[i].h < 0) || (thePixelShatter[i].h >= (640 * 16)) ||
  1628.             (thePixelShatter[i].v < 0) || (thePixelShatter[i].v >= (480 * 16)))
  1629.         {
  1630.             thePixelShatter[i] = thePixelShatter[numPixelShatter];
  1631.             numPixelShatter--;
  1632.             continue;
  1633.         }
  1634.         
  1635.         i++;
  1636.     }
  1637. }
  1638.  
  1639. void StartPixelShatterRect(Rect *r, short dH, short dV, short type)
  1640. {
  1641.     StartPixelShatter(r->left, r->top, dH, dV, type);
  1642.     StartPixelShatter(r->left, r->bottom, dH, dV, type);
  1643.     StartPixelShatter(r->right, r->top, dH, dV, type);
  1644.     StartPixelShatter(r->right, r->bottom, dH, dV, type);
  1645.     StartPixelShatter((r->left + r->right) / 2, (r->top + r->bottom) / 2, dH, dV, type);    
  1646. }
  1647.  
  1648. void StartPixelShatter(short h, short v, short hVel, short vVel, short type)
  1649.     signed short hOffset[9] = {0,16,16,0,-16,-16,-16,0,16};
  1650.     signed short vOffset[9] = {0,0,-16,-16,-16,0,16,16,16};    
  1651.     int itr;
  1652.     unsigned char color;
  1653.     
  1654.     h *= 16;
  1655.     v *= 16;
  1656.  
  1657.     if ((numPixelShatter + 10) >= kMaxPixelShatter)
  1658.     {
  1659.         return;
  1660.     }
  1661.     
  1662.     
  1663.     if (    (type == kShatterLavaBubble) ||
  1664.             (type == kShatterLavaSplash) ||
  1665.             (type == kShatterLavaBurst) ||
  1666.             (type == kShatterPlayerDeath) ||
  1667.             (type == kShatterEnemyDeath))
  1668.     {
  1669.         color = 215;
  1670.     }
  1671.     else if (type == kShatterPlatformLightning)
  1672.     {
  1673.         color = 5;
  1674.     }
  1675.     else
  1676.     {
  1677.         color = 0;
  1678.     }
  1679.  
  1680.  
  1681.     for(itr = 0; itr < 9; itr++)
  1682.     {
  1683.         thePixelShatter[numPixelShatter].h = h + hOffset[itr];
  1684.         thePixelShatter[numPixelShatter].v = v + vOffset[itr];
  1685.         thePixelShatter[numPixelShatter].h = h + hOffset[itr] + RandomInt(20) - 10;
  1686.         thePixelShatter[numPixelShatter].v = v + vOffset[itr] + RandomInt(20) - 10;
  1687.         thePixelShatter[numPixelShatter].hVel = hVel + hOffset[itr];
  1688.         thePixelShatter[numPixelShatter].vVel = vVel + vOffset[itr];
  1689.         thePixelShatter[numPixelShatter].color = color;
  1690.         
  1691.         if ((type == kShatterLavaSplash) || (type == kShatterPlatformLightning))
  1692.         {
  1693.             thePixelShatter[numPixelShatter].duration = 400;
  1694.         }
  1695.         else if ((type == kShatterPlatformScrape) || (type == kShatterPlatformHit))
  1696.         {
  1697.             thePixelShatter[numPixelShatter].duration = RandomInt(15) + 10;
  1698.         }
  1699.         else
  1700.         {
  1701.             thePixelShatter[numPixelShatter].duration = RandomInt(21) + 20;
  1702.         }
  1703.         thePixelShatter[numPixelShatter].type = type;
  1704.         
  1705.         numPixelShatter++;
  1706.     }
  1707. }
  1708.  
  1709. void HandleScoreFloaters(void)
  1710. {
  1711.     int i = 0;
  1712.     
  1713.     while(i < numScoreFloater)
  1714.     {
  1715.         if (theScoreFloater[i].duration == 0)
  1716.         {
  1717.             theScoreFloater[i] = theScoreFloater[numScoreFloater];
  1718.             numScoreFloater--;
  1719.             continue;
  1720.         }
  1721.         
  1722.         theScoreFloater[i].location.v -= 2;
  1723.         theScoreFloater[i].duration--;
  1724.         i++;
  1725.     }
  1726. }
  1727.  
  1728. void StartScoreFloater(unsigned long score, Point where)
  1729. {
  1730.     if (where.h > 419) { where.h = 419; }
  1731.     if (where.v > 459) { where.v = 549; }
  1732.     if (where.h < 0) {  where.h = 0;}
  1733.     if (where.v < 0) { where.v = 0; }
  1734.     
  1735.     theScoreFloater[numScoreFloater].score = score;
  1736.     theScoreFloater[numScoreFloater].location = where;
  1737.     theScoreFloater[numScoreFloater].duration = 20;
  1738.     numScoreFloater++;
  1739. }