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

  1. /*
  2.     File:        SprocketInvaders.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.         (cjd)    Chris DeSalvo
  21.         (BWS)    Brent Schorsch
  22.  
  23.     Change History (most recent first):
  24.  
  25.         <17>      7/2/98    BWS        fix 68k build (no SSp toggle)
  26.         <16>      7/1/98    cjd        Made it so that you can shoot enemy shots, added optimization
  27.                                     for collission of player shots and enemies, added new FPS
  28.                                     display for SSp usage.
  29.         <15>     6/12/98    BWS        InputSprocket 68k now used
  30. */
  31.  
  32. //•    ------------------------------------------------------------------------------------------    •
  33. //•
  34. //•    Copyright © 1996 Apple Computer, Inc., All Rights Reserved
  35. //•
  36. //•
  37. //•        You may incorporate this sample code into your applications without
  38. //•        restriction, though the sample code has been provided "AS IS" and the
  39. //•        responsibility for its operation is 100% yours.  However, what you are
  40. //•        not permitted to do is to redistribute the source as "DSC Sample Code"
  41. //•        after having made changes. If you're going to re-distribute the source,
  42. //•        we require that you make it clear in the source that the code was
  43. //•        descended from Apple Sample Code, but that you've made changes.
  44. //•
  45. //•        Authors:
  46. //•            Chris De Salvo
  47. //•            Jamie Osborne
  48. //•            Michael Evans
  49. //•            Tim Carroll
  50. //•
  51. //•    ------------------------------------------------------------------------------------------    •
  52.  
  53. //•    ------------------------------    Includes
  54.  
  55. #include <Fonts.h>
  56. #include <stdio.h>
  57. #include <string.h>
  58.  
  59. #include "ErrorHandler.h"
  60. #include "EventHandler.h"
  61. #include "GameObject.h"
  62. #include "Graphics.h"
  63. #include "MoviePlayback.h"
  64. #include "ObjectActions.h"
  65. #include "RedbookHandler.h"
  66. #include "SIResources.h"
  67. #include "SoundHandler.h"
  68. #include "SprocketInvaders.h"
  69. #include "Sprite.h"
  70. #include "NetSprocketSupport.h"
  71. #include "CommonStuff.h"
  72.  
  73. //•    ------------------------------    Private Definitions
  74.  
  75. #define RAND(x)                        ((Random() & 0x7FFF) % (x))
  76.  
  77. #define kPlayerVelocity                2L    //•    Speed at which player moves
  78. #define kPlayerShotVelocity            10L    //•    Speed at which player shots travel
  79. #define kPointsVelocity                -2L    //•    Vertical speed of the "points" object (negative is up)
  80. #define kEnemyVelocity                5L    //•    Speed at which enemies move
  81. #define kEnemyShotVelocity            4L    //•    Speed at which enemy shots drop
  82.  
  83. #define kEnemyOffset                8    //•    Space between columns of enemies
  84.  
  85. #define kEnemyParticleColor            200
  86. #define kPlayerParticleColor        192
  87. #define kPlasmaParticleColor        208
  88. #define kMissileParticleColor        216
  89.  
  90. //•    ------------------------------    Private Types
  91.  
  92. enum
  93. {
  94.     scPlayer,
  95.     scPlayer2,
  96.     scPlayerShot,
  97.     scPoints,
  98.     scEnemy,
  99.     scEnemyShot,
  100.     scNumSprites
  101. };
  102.  
  103. //•    ------------------------------    Private Variables
  104.  
  105. static SpritePtr    gSpriteCache[scNumSprites];    //•    Sprite artwork is cached here so that multiple items using
  106.                                                 //•    the same artwork only need one copy.
  107.  
  108. static UInt32    gWave;                    //•    Current level number
  109. static UInt32    gEnemyGas;                //•    Number of enemies that get updated per game loop
  110. static SInt32    gEnemyNewDirection;        //•    If enemies changed directions, this is the new direction
  111. static UInt32    gFrameRateBaseTime;        //•    Base time for this game to count frame rate
  112.  
  113. static float    gGreenAccum;
  114. static float    gRedAccum;
  115.  
  116. //•    ------------------------------    Private Functions
  117.  
  118. static void AddPlayers(void);
  119. static void AddEnemies(void);
  120. static void CollideShotsToEnemies(void);
  121. static void CollideShotsToPlayers(void);
  122. static void CollideShotsToShots(void);
  123. static void AddPoints(short x, short y);
  124. static void DisplayGameOver(CGrafPtr backBuff);
  125. static void DropEnemies(void);
  126. static void AdvanceEnemies(void);
  127. static void DisplayFrameRate(void);
  128. static void EndGame(CGrafPtr backBuff);
  129. static void GetGreenInput(void);
  130. static void GetRedInput(void);
  131. static Boolean WasButtonHit (ISpElementReference inElement);
  132. static void BuildEnemyRect(void);
  133.  
  134. //•    ------------------------------    Public Variables
  135.  
  136. Boolean    gGameInProgress = false;
  137. Boolean    gTwoPlayers = false;
  138.  
  139. GameObjectPtr    gEnemyList = nil;                //•    List of enemies
  140. GameObjectPtr    gEnemyShotList = nil;            //•    List of enemy shots
  141. GameObjectPtr    gPlayerList = nil;                //•    List of players
  142. GameObjectPtr    gGreenPlayerShotList = nil;        //•    List of green player shots
  143. GameObjectPtr     gRedPlayerShotList = nil;        //•    List of red player shots
  144. GameObjectPtr    gMiscObjectList = nil;            //•    List of misc items (score sprites, etc)
  145.  
  146. Rect            gEnemyRect;                        //•    Rect enclosing all enemies
  147.  
  148. UInt32            gEnemyTask;
  149. SInt32            gEnemiesChangeDirection;
  150. SInt32            gEnemyVelocity;
  151. UInt32            gNumEnemies;
  152.  
  153. UInt32            gEnemyLevel;
  154. UInt32            gNumEnemiesProcessed;
  155.  
  156. UInt32            gNumGreenPlayerLives = 0;
  157. UInt32            gNumRedPlayerLives = 0;
  158.  
  159. Boolean            gNetPlay;
  160.  
  161. //•    --------------------    InitNewRound
  162.  
  163. static void
  164. InitNewRound(UInt32 wave)
  165. {
  166. SInt16    numCDTracks;
  167.  
  168.     //•    Flush out our object lists
  169.     GameObjectDisposeList(&gEnemyList);
  170.     GameObjectDisposeList(&gEnemyShotList);
  171.     GameObjectDisposeList(&gPlayerList);
  172.     GameObjectDisposeList(&gGreenPlayerShotList);
  173.     GameObjectDisposeList(&gRedPlayerShotList);
  174.     GameObjectDisposeList(&gMiscObjectList);
  175.  
  176.     //•    Add our initial objects
  177.     AddPlayers();
  178.     AddEnemies();
  179.  
  180.     gEnemyTask = kEnemyMovingRight;
  181.     gEnemiesChangeDirection = 0;
  182.     gEnemyVelocity = kEnemyVelocity;
  183.     gEnemyLevel = 1;
  184.     gWave = wave;
  185.  
  186.     //•    Pick a CD audio track and start playback if CD audio was chosen
  187.     numCDTracks = RedbookGetNumTracks();
  188.     if (numCDTracks > 0)
  189.     {
  190.     SInt16    trackNum;
  191.     
  192.         if (wave > numCDTracks)
  193.         {
  194.             trackNum = (wave % numCDTracks) + 1;
  195.         }
  196.         else
  197.         {
  198.             trackNum = wave;
  199.         }
  200.         
  201.         //•    Unnecessary error checking
  202.         if (trackNum < 1)
  203.             trackNum = 1;
  204.             
  205.         if (trackNum > numCDTracks)
  206.             trackNum = numCDTracks;
  207.             
  208.         RedbookPlayTrackNum(trackNum);
  209.         
  210.     }
  211.  
  212.     SetRect(&gEnemyRect, 0, 0, 0, 0);
  213. }
  214.  
  215. //•    --------------------    NextRound
  216.  
  217. static void
  218. NextRound(void)
  219. {
  220.     InitNewRound(gWave + 1);
  221. }
  222.  
  223.  
  224. //•    --------------------    InitNewGame
  225.  
  226. void
  227. InitNewGame(UInt32 wave)
  228. {
  229. UInt32    i;
  230.     
  231.     //•    Start up the background movie if one was chosen
  232.     PlaybackMovie();
  233.     
  234.     for (i = 0; i < numInputs; i++)
  235.         ISpElement_Flush(gInputElements[i]);
  236.     
  237.     gGreenAccum = 0;
  238.     gRedAccum = 0;
  239.  
  240.     gGameInProgress = true;
  241.     gFrameRateBaseTime = 0L;
  242.  
  243.     //•    Flush out the sprite cache and load in the new sprites
  244.     for (i = 0; i < scNumSprites; i++)
  245.         SpriteDispose(&(gSpriteCache[i]));
  246.  
  247.     gSpriteCache[scPlayer] = SpriteLoad(kSPRTPlayer);
  248.     if (! gSpriteCache[scPlayer])
  249.         FatalError("Could not load player tank.");
  250.  
  251.     gSpriteCache[scPlayer2] = SpriteLoad(kSPRTPlayer2);
  252.     if (! gSpriteCache[scPlayer])
  253.         FatalError("Could not load player 2 tank.");
  254.  
  255.     gSpriteCache[scPlayerShot] = SpriteLoad(kSPRTPlayerShot);
  256.     if (! gSpriteCache[scPlayerShot])
  257.         FatalError("Could not load player shot.");
  258.         
  259.     gSpriteCache[scPoints] = SpriteLoad(kSPRTPoints);
  260.     if (! gSpriteCache[scPoints])
  261.         FatalError("Could no load points.");
  262.  
  263.     gSpriteCache[scEnemy] = SpriteLoad(kSPRTEnemy);
  264.     if (! gSpriteCache[scEnemy])
  265.         FatalError("Could no load enemy.");
  266.  
  267.     gSpriteCache[scEnemyShot] = SpriteLoad(kSPRTEnemyShot);
  268.     if (! gSpriteCache[scEnemyShot])
  269.         FatalError("Could no load enemy shot.");
  270.  
  271.     gNumGreenPlayerLives = 3;
  272.     
  273.     if (gTwoPlayers)
  274.         gNumRedPlayerLives = 3;
  275.     else
  276.         gNumRedPlayerLives = 0;
  277.     
  278.     //•    Activate our graphics context and tell InputSprocket to start
  279.     //•    watching our controllers
  280.     GraphicsActive();
  281.     ISpResume();
  282.     
  283.     InitNewRound(wave);
  284. }
  285.  
  286. #pragma mark -
  287.  
  288. //•    --------------------    GameLoop
  289.  
  290. void
  291. GameLoop(void)
  292. {
  293. CGrafPtr        backBuff;
  294. ISpElementEvent    event;
  295. Boolean            wasEvent;
  296.         
  297.     if (WasButtonHit(gInputElements[soundToggle]))
  298.         gSoundEffects = !gSoundEffects;
  299.  
  300.     if (gNetPlay)
  301.     {
  302.         if (gIAmHost)
  303.         {
  304.             GetGreenInput();
  305.             SendInputState(gGameKeys.greenLeft, gGameKeys.greenRight, gGameKeys.greenFire);
  306.             GetRedInput();
  307.         }
  308.         else
  309.         {
  310.             GetRedInput();
  311.             SendInputState(gGameKeys.redLeft, gGameKeys.redRight, gGameKeys.redFire);
  312.             GetGreenInput();
  313.         }    
  314.     }
  315.     else
  316.     {
  317.         GetGreenInput();
  318.         if (gTwoPlayers)
  319.             GetRedInput();
  320.     }
  321.     
  322.  
  323. #if GENERATINGPOWERPC
  324.     //•    Load in the current SoundSprocket throttle value
  325.     ISpElement_GetSimpleState(gInputElements[soundSprocketThrottle], &gCPUCurrentLoad);
  326.  
  327.     if (0 == gCPULoadModifier)
  328.         gCPUCurrentLoad = 0;
  329.     else
  330.         gCPUCurrentLoad /= gCPULoadModifier;
  331. #endif
  332.  
  333.     //•    Perform player/missile collission detection
  334.     CollideShotsToEnemies();
  335.     CollideShotsToShots();
  336.     CollideShotsToPlayers();
  337.  
  338.     //•    If any enemy hit a boundry on the last run, switch directions
  339.     if (gEnemiesChangeDirection)
  340.     {
  341.         DropEnemies();
  342.         gEnemiesChangeDirection = 0;
  343.         gEnemyTask = kEnemyDropping;
  344.     }
  345.  
  346.     //•    gEnemyGas determines how many aliens are processed each game loop
  347.     //•    This gives the staggered updating on the screen.  It also speeds up the
  348.     //•    alien movement and shooting speed as the game progresses.
  349.     gEnemyGas = gWave + 1;
  350.  
  351.     ServiceMoviePlayback();
  352.  
  353.     //•    Get a reference to the back buffer so we can draw into it
  354.     DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuff);
  355.  
  356.     //•    Check the abort key in case the user wishes to quit out early
  357.     ISpElement_GetNextEvent(gInputElements[abort], sizeof (event), &event, &wasEvent);
  358.     if (gGotEndGameMessage || (wasEvent && (event.data == kISpButtonDown)))
  359.     {
  360.         if (gNetPlay && !gGotEndGameMessage)
  361.             SendEndGame();
  362.  
  363.         EndGame(backBuff);
  364.         return;
  365.     }
  366.  
  367.     //•    Have all game objects perform their actions
  368.     AdvanceEnemies();
  369.     GameObjectListAdvance(gPlayerList);
  370.     GameObjectListAdvance(gEnemyShotList);
  371.     GameObjectListAdvance(gGreenPlayerShotList);
  372.     GameObjectListAdvance(gRedPlayerShotList);
  373.     GameObjectListAdvance(gMiscObjectList);
  374.  
  375.     //•    Draw all game objects
  376.     GameObjectListDraw(gEnemyList, backBuff);
  377.     GameObjectListDraw(gPlayerList, backBuff);
  378.     GameObjectListDraw(gEnemyShotList, backBuff);
  379.     GameObjectListDraw(gGreenPlayerShotList, backBuff);
  380.     GameObjectListDraw(gRedPlayerShotList, backBuff);
  381.     GameObjectListDraw(gMiscObjectList, backBuff);
  382.  
  383.     //•    Build a master rect of all the enemies to help optimize hit testing
  384.     //•    the next time through the loop.
  385.     BuildEnemyRect();
  386.  
  387.     //•    Check for end of wave/game
  388.     if ((gNumGreenPlayerLives < 1) && (gNumRedPlayerLives < 1))
  389.     {
  390.         EndGame(backBuff);
  391.         return;
  392.     }
  393.     
  394.     //•    If we just killed the last enemy then start the next round
  395.     if (gNumEnemies < 1)
  396.     {
  397.         NextRound();
  398.     }
  399.  
  400.     //•    Update the frame rate display information
  401.     DisplayFrameRate();
  402.  
  403.     //•    Redraw the entire screen.  This is done last so that if the game over screen was drawn it gets blit for free
  404.     GraphicsUpdateScreen();
  405.  
  406. }
  407.  
  408. //•    --------------------    GetAutoFireButton
  409.  
  410. static Boolean
  411. GetAutoFireButton(ISpElementReference inElement)
  412. {
  413. OSStatus    error;
  414. UInt32        input;
  415. Boolean        wasEvent;
  416. Boolean        fire = false;
  417.     
  418.     //•    poll    
  419.     error = ISpElement_GetSimpleState(inElement, &input);
  420.     if (! error && (input == kISpButtonDown)) 
  421.         fire = true;
  422.  
  423.     //•    but don't miss fast clicks or macros
  424.     do
  425.     {
  426.     ISpElementEvent    event;
  427.  
  428.         error = ISpElement_GetNextEvent(inElement, sizeof(ISpElementEvent), &event, &wasEvent);
  429.  
  430.         if (! error && wasEvent && (event.data == kISpButtonDown))
  431.         {
  432.             fire = true; 
  433.             break;
  434.         }
  435.     } while (wasEvent && !error);
  436.     
  437.     //•    flush the queue
  438.     ISpElement_Flush(inElement);
  439.     
  440.     return (fire);
  441. }
  442.  
  443. //•    --------------------    WasButtonHit
  444.  
  445. static Boolean
  446. WasButtonHit (ISpElementReference inElement)
  447. {
  448. OSStatus    error;
  449. Boolean        wasEvent;
  450. Boolean        down = false;
  451.     
  452.     do
  453.     {
  454.     ISpElementEvent    event;
  455.  
  456.         error = ISpElement_GetNextEvent(inElement, sizeof(ISpElementEvent), &event, &wasEvent);
  457.  
  458.         if (! error && wasEvent && (event.data == kISpButtonDown))
  459.         {
  460.             down = true; 
  461.             break;
  462.         }
  463.     } while (wasEvent && !error);
  464.     
  465.     return (down);
  466. }
  467.  
  468. //•    --------------------    GetGreenInput
  469.  
  470. void
  471. GetGreenInput(void)
  472. {
  473. UInt32    input;
  474.     
  475.     if (! gNetPlay || (gNetPlay && gIAmHost))    // in netplay, the host is the green player, and the joiner the red
  476.     {
  477.         //•    Check the movement axis
  478.         ISpElement_GetSimpleState(gInputElements[greenMovement], &input);
  479.  
  480.         gGameKeys.greenLeft = false;
  481.         gGameKeys.greenRight = false;
  482.         
  483.         if (input < 0x2FFFFFFF)
  484.         {
  485.             gGameKeys.greenLeft = true;
  486.         }
  487.         else if (input > 0xBFFFFFFF)
  488.         {
  489.             gGameKeys.greenRight =  true;
  490.         }
  491.         else if (input < 0x5FFFFFFF)
  492.         { 
  493.             gGreenAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x3FFFFFFF;
  494.              if (gGreenAccum < -1)
  495.              {
  496.                  gGameKeys.greenLeft = true;
  497.                  gGreenAccum += 1;
  498.              }
  499.          }
  500.          else if (input > 0x9FFFFFFF)
  501.          {
  502.             gGreenAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x3FFFFFFF;
  503.              if (gGreenAccum > 1)
  504.              {
  505.                  gGameKeys.greenRight = true;
  506.                  gGreenAccum -= 1;
  507.              }
  508.          }
  509.         
  510.         //•    Check the fire button
  511.         gGameKeys.greenFire = GetAutoFireButton(gInputElements[greenFire]);
  512.     }
  513.     else if (gNetPlay && !gIAmHost)    //•    wait for player 1's input
  514.     {
  515.         gReceivedInput = false;
  516.         while (! gReceivedInput && !gGotEndGameMessage)
  517.             HandleNetworking();
  518.     }
  519. }
  520.  
  521. //•    --------------------    GetRedInput
  522.  
  523. void GetRedInput(void)
  524. {
  525. UInt32    input;
  526.  
  527.     if (! gNetPlay || (gNetPlay && !gIAmHost))    // in netplay, the host is the green player, and the joiner the red
  528.     {
  529.         //•    Check the movement axis
  530.         ISpElement_GetSimpleState(gInputElements[redMovement], &input);
  531.  
  532.         gGameKeys.redLeft = false;
  533.         gGameKeys.redRight = false;
  534.         
  535.         if (input < 0x3FFFFFFF)
  536.         {
  537.             gGameKeys.redLeft = true;
  538.         }
  539.         else if (input > 0xAFFFFFFF)
  540.         {
  541.             gGameKeys.redRight =  true;
  542.         }
  543.         else if (input < 0x5FFFFFFF)
  544.         { 
  545.             gRedAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x2FFFFFFF;
  546.              if (gRedAccum < -1)
  547.              {
  548.                  gGameKeys.redLeft = true;
  549.                  gRedAccum += 1;
  550.              }
  551.          }
  552.          else if (input > 0x9FFFFFFF)
  553.          {
  554.             gRedAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x2FFFFFFF;
  555.              if (gRedAccum > 1)
  556.              {    
  557.                  gGameKeys.redRight = true;
  558.                  gRedAccum -= 1;
  559.              }
  560.          }
  561.  
  562.         //•    Check the fire button
  563.         gGameKeys.redFire = GetAutoFireButton(gInputElements[redFire]);
  564.     }
  565.     else if (gNetPlay && gIAmHost)    //•    wait for player 2's input
  566.     {
  567.         gReceivedInput = false;
  568.         while (! gReceivedInput && !gGotEndGameMessage)
  569.             HandleNetworking();
  570.     }
  571. }
  572.  
  573. //•    --------------------    PlayerShoot
  574.  
  575. void
  576. PlayerShoot(GameObjectPtr whichPlayer)
  577. {
  578. Rect            r;
  579. GameObjectPtr    go;
  580. GameObjectPtr    shotList;
  581.  
  582.  
  583.     if (whichPlayer->kind == objectGreenPlayer)
  584.     {
  585.         if (gGreenPlayerShotList)
  586.             return;
  587.         else
  588.             shotList = gGreenPlayerShotList;
  589.     }
  590.     else
  591.     {
  592.         if (gRedPlayerShotList)
  593.             return;
  594.         else
  595.             shotList = gRedPlayerShotList;
  596.     }
  597.  
  598.     SoundHandlerPlay(soundPlayerFire, whichPlayer->screenX, whichPlayer->screenY);
  599.  
  600.     go = GameObjectAllocate();
  601.     if (! go)
  602.         FatalError("Could not allocate player shot.");
  603.         
  604.     GameObjectAddToList(&shotList, go);
  605.     
  606.     SetRect(&r, 20, 0, 620, 0);
  607.     GameObjectSetBounds(go, &r);
  608.     GameObjectSetSprite(go, gSpriteCache[scPlayerShot]);
  609.     
  610.     go->screenX = whichPlayer->screenX;
  611.     go->screenY = whichPlayer->screenY - SpriteHeight(whichPlayer->objectData.sprite, whichPlayer->frame) - 1;
  612.     go->velocityH = 0;
  613.     go->velocityV = kPlayerShotVelocity;
  614.     go->action = PlayerShotAction;
  615.     
  616.     if (whichPlayer->kind == objectRedPlayer)
  617.     {
  618.         go->kind = objectRedPlayerShot;
  619.         if (! gRedPlayerShotList)
  620.             gRedPlayerShotList = shotList;
  621.     }
  622.     else
  623.     {
  624.         go->kind = objectGreenPlayerShot;
  625.         if (! gGreenPlayerShotList)
  626.             gGreenPlayerShotList = shotList;
  627.     }
  628. }
  629.  
  630. //•    --------------------    AdvanceEnemies
  631.  
  632. static void
  633. AdvanceEnemies(void)
  634. {
  635. GameObjectPtr    current, next;
  636. UInt32            oldProcessed;
  637.  
  638.     //•    This set of loops makes sure that gEnemyGas worth
  639.     //•    of aliens get processed this loop.  If gEnemyGas is set to
  640.     //•    10, and there are only 5 aliens left then they all update twice
  641.     //•    for a total of ten updates.  This is how we take care us speeeding
  642.     //•    the aliens up as fewer and fewer of them are present.
  643.     
  644.     do
  645.     {
  646.         gNumEnemiesProcessed = 0;
  647.         current = gEnemyList;
  648.         
  649.         do
  650.         {
  651.             oldProcessed = gNumEnemiesProcessed;
  652.             next = current->next;
  653.             current->action(current);
  654.             
  655.             //•    If we moved an alien, use up some alien gas
  656.             if (oldProcessed != gNumEnemiesProcessed)
  657.                 gEnemyGas--;
  658.                 
  659.             current = next;
  660.         } while (next && gEnemyGas);
  661.         
  662.         //•    If no enemies were processed then they're all at the same level, so move on
  663.         if (gNumEnemiesProcessed == 0)
  664.         {
  665.             gEnemyLevel++;
  666.             
  667.             if (gEnemyTask == kEnemyDropping)
  668.                 gEnemyTask = gEnemyNewDirection;
  669.         }
  670.  
  671.     } while (gEnemyGas && (gNumEnemies > 0));
  672. }
  673.  
  674. //•    --------------------    DropEnemies
  675.  
  676. static void
  677. DropEnemies(void)
  678. {
  679. GameObjectPtr    current, next;
  680.  
  681.     //•    One of the enemies hit the edge of their boundary so we need
  682.     //•    to draw them all one row.
  683.     gEnemyNewDirection = gEnemiesChangeDirection;
  684.  
  685.     do
  686.     {
  687.         current = gEnemyList;
  688.         gNumEnemiesProcessed = 0;
  689.         
  690.         do
  691.         {
  692.             next = current->next;
  693.             current->action(current);
  694.             current = next;
  695.         } while (next);
  696.     } while (gNumEnemiesProcessed);
  697.  
  698.     gEnemyLevel++;
  699. }
  700.  
  701. //•    --------------------    EnemyShoot
  702.  
  703. void
  704. EnemyShoot(GameObjectPtr whichEnemy)
  705. {
  706. Rect            r;
  707. GameObjectPtr    go;
  708.  
  709.     //•    Create a game object for the shot
  710.     go = GameObjectAllocate();
  711.     if (! go)
  712.         FatalError("Could not allocate enemy shot.");
  713.         
  714.     //•    Play the alien shooting sound effect
  715.     SoundHandlerPlay(soundEnemyFire, whichEnemy->screenX, whichEnemy->screenY);
  716.  
  717.     //•    Add the shot to the enemy shot list
  718.     GameObjectAddToList(&gEnemyShotList, go);
  719.     
  720.     //•    Set the attributes for this object
  721.     SetRect(&r, 20, 0, 620, 412);
  722.     GameObjectSetBounds(go, &r);
  723.     GameObjectSetSprite(go, gSpriteCache[scEnemyShot]);
  724.     
  725.     go->screenX = whichEnemy->screenX;
  726.     go->screenY = whichEnemy->screenY + 1;
  727.     go->velocityH = 0;
  728.     go->velocityV = kEnemyShotVelocity;
  729.     go->action = EnemyShotAction;
  730. }
  731.  
  732. //•    --------------------    CollideShotsToEnemies
  733.  
  734. static void
  735. CollideShotsToEnemies(void)
  736. {
  737. GameObjectPtr    shot;
  738. GameObjectPtr    target;
  739. Rect            dummyRect;
  740.  
  741.     if (! gEnemyList)
  742.         return;
  743.         
  744.     //•    Iterate over the green player shots
  745.     if (gGreenPlayerShotList)
  746.     {
  747.         for (shot = gGreenPlayerShotList; shot; shot = shot->next)
  748.         {
  749.             //•    Quick easy check to see if the shot is even within the block of enemies
  750.             if (false == SectRect(&shot->screenRect, &gEnemyRect, &dummyRect))
  751.                 continue;
  752.         
  753.             //•    Iterate over the enemies
  754.             for (target = gEnemyList; target; target = target->next)
  755.             {
  756.                 if (IntersectRects(&shot->screenRect, &target->screenRect))
  757.                 {
  758.                     SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
  759.                     
  760.                     shot->action = PlayerShotDestroy;
  761.                     target->action = EnemyDestroy;
  762.                     AddPoints(target->screenX, target->screenY);
  763.                 }
  764.             }
  765.         }
  766.     }
  767.     
  768.     //•    Iterate over the red player shots
  769.     if (gRedPlayerShotList)
  770.     {
  771.         for (shot = gRedPlayerShotList; shot; shot = shot->next)
  772.         {
  773.             //•    Quick easy check to see if the shot is even within the block of enemies
  774.             if (false == SectRect(&shot->screenRect, &gEnemyRect, &dummyRect))
  775.                 continue;
  776.         
  777.             //•    Iterate over the enemies
  778.             for (target = gEnemyList; target; target = target->next)
  779.             {
  780.                 if (IntersectRects(&shot->screenRect, &target->screenRect))
  781.                 {
  782.                     SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
  783.                     
  784.                     shot->action = PlayerShotDestroy;
  785.                     target->action = EnemyDestroy;
  786.                     AddPoints(target->screenX, target->screenY);
  787.                 }
  788.             }
  789.         }
  790.     }
  791. }
  792.  
  793. //•    --------------------    CollideShotsToShots
  794.  
  795. static void
  796. CollideShotsToShots(void)
  797. {
  798. GameObjectPtr    shot;
  799. GameObjectPtr    target;
  800.  
  801.     if (nil == gEnemyShotList)
  802.         return;
  803.         
  804.     //•    Iterate over the green player shots
  805.     if (gGreenPlayerShotList)
  806.     {
  807.         for (shot = gGreenPlayerShotList; shot; shot = shot->next)
  808.         {
  809.             //•    Iterate over the enemies
  810.             for (target = gEnemyShotList; target; target = target->next)
  811.             {
  812.                 if (IntersectRects(&shot->screenRect, &target->screenRect))
  813.                 {
  814.                     shot->action = PlayerShotDestroy;
  815.                     target->action = EnemyShotDestroy;
  816.                 }
  817.             }
  818.         }
  819.     }
  820.     
  821.     //•    Iterate over the red player shots
  822.     if (gRedPlayerShotList)
  823.     {
  824.         for (shot = gRedPlayerShotList; shot; shot = shot->next)
  825.         {
  826.             //•    Iterate over the enemies
  827.             for (target = gEnemyShotList; target; target = target->next)
  828.             {
  829.                 if (IntersectRects(&shot->screenRect, &target->screenRect))
  830.                 {
  831.                     SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
  832.                     
  833.                     shot->action = PlayerShotDestroy;
  834.                     target->action = EnemyShotDestroy;
  835.                 }
  836.             }
  837.         }
  838.     }
  839. }
  840. //•    --------------------    CollideShotsToPlayers
  841.  
  842. static void
  843. CollideShotsToPlayers(void)
  844. {
  845. GameObjectPtr    shot;
  846. GameObjectPtr    target;
  847. GameObjectPtr    next;
  848.  
  849.     if (! gEnemyShotList || ! gPlayerList)
  850.         return;
  851.  
  852.     target = gPlayerList;
  853.     next = target->next;
  854.  
  855.     while (target != nil)
  856.     {
  857.         if (target->refCon > 0)
  858.             return;
  859.  
  860.         //•    Iterate over the enemy shots
  861.         for (shot = gEnemyShotList; shot; shot = shot->next)
  862.         {
  863.             if (IntersectRects(&shot->screenRect, &target->screenRect))
  864.             {
  865.                 SoundHandlerPlay(soundPlayerHit,target->screenX, target->screenY);
  866.  
  867.                 shot->action = EnemyShotDestroy;
  868.                 target->action = PlayerDestroy;
  869.                 
  870.                 if (target->kind == objectGreenPlayer)
  871.                 {
  872.                     gNumGreenPlayerLives--;
  873.                     if (gNumGreenPlayerLives < 1)
  874.                         GameObjectRemoveFromList(&gPlayerList, target);
  875.                 }
  876.                 else
  877.                 {
  878.                     gNumRedPlayerLives--;
  879.                     if (gNumRedPlayerLives < 1)
  880.                         GameObjectRemoveFromList(&gPlayerList, target);
  881.                 }
  882.             }
  883.         }
  884.         
  885.         target = next;
  886.         next = target->next;
  887.     }
  888. }
  889.  
  890. //•    --------------------    BuildEnemyRect
  891.  
  892. static void
  893. BuildEnemyRect(void)
  894. {
  895. Rect            workingRect = { 0, 0, 0, 0 };
  896. GameObjectPtr    currentEnemy;
  897.  
  898.     if (nil != gEnemyList)
  899.         currentEnemy = gEnemyList;
  900.     else
  901.         return;
  902.         
  903.     workingRect = currentEnemy->screenRect;
  904.     
  905.     do
  906.     {
  907.         UnionRect(&workingRect, ¤tEnemy->screenRect, &workingRect);
  908.         currentEnemy = currentEnemy->next;
  909.     } while (nil != currentEnemy);
  910.     
  911.     gEnemyRect = workingRect;
  912. }
  913.  
  914. #pragma mark -
  915.  
  916. //•    --------------------    AddPlayers
  917.  
  918. static void
  919. AddPlayers(void)
  920. {
  921. Rect            r;
  922. GameObjectPtr    go;
  923.  
  924.     go = GameObjectAllocate();
  925.     if (! go)
  926.         FatalError("Could not allocate player object.");
  927.         
  928.     GameObjectAddToList(&gPlayerList, go);
  929.     
  930.     SetRect(&r, 20, 0, 620, 0);
  931.     GameObjectSetBounds(go, &r);
  932.     GameObjectSetSprite(go, gSpriteCache[scPlayer]);
  933.     
  934.     go->kind = objectGreenPlayer;
  935.     go->screenX = 20;
  936.     go->screenY = 412;
  937.     go->velocityH = kPlayerVelocity;
  938.     go->velocityV = 0;
  939.     go->action = GreenPlayerAction;
  940.  
  941.     //•    Sloppy way to do this, but hey, I'm in a hurry
  942.     if (gTwoPlayers)
  943.     {
  944.         go = GameObjectAllocate();
  945.         if (! go)
  946.             FatalError("Could not allocate player 2 object.");
  947.             
  948.         GameObjectAddToList(&gPlayerList, go);
  949.         
  950.         SetRect(&r, 20, 0, 620, 0);
  951.         GameObjectSetBounds(go, &r);
  952.         GameObjectSetSprite(go, gSpriteCache[scPlayer2]);
  953.         
  954.         go->kind = objectRedPlayer;
  955.         go->screenX = 640 - 20;
  956.         go->screenY = 412;
  957.         go->velocityH = kPlayerVelocity;
  958.         go->velocityV = 0;
  959.         go->action = RedPlayerAction;
  960.     }
  961. }
  962.  
  963. //•    --------------------    AddEnemies
  964.  
  965. static void
  966. AddEnemies(void)
  967. {
  968. Rect            r;
  969. GameObjectPtr    go;
  970. UInt32            i, j;
  971. UInt32            w, h;
  972. UInt32            x, x2, y;
  973.  
  974.     gNumEnemies = 0;
  975.  
  976.     //•    Find dimensions of enemy sprite so we can center the rows to begin with
  977.     w = SpriteWidth(gSpriteCache[scEnemy], 0);
  978.     w += kEnemyOffset;
  979.     h = SpriteHeight(gSpriteCache[scEnemy], 0);
  980.     h += kEnemyOffset;
  981.     
  982.     x2 = w * kNumEnemyColumns;
  983.     x2 = 320 - (x2 / 2);
  984.  
  985.     y = 100;
  986.  
  987.     SetRect(&r, 20, 20, 620, 400);
  988.  
  989.     for (i = 0; i < kNumEnemyRows; i++)
  990.     {
  991.         x = x2;
  992.         
  993.         for (j = 0; j < kNumEnemyColumns; j++)
  994.         {
  995.             go = GameObjectAllocate();
  996.             if (! go)
  997.                 FatalError("Could not allocate enemy object.");
  998.             
  999.             GameObjectAddToList(&gEnemyList, go);
  1000.             
  1001.             GameObjectSetBounds(go, &r);
  1002.             GameObjectSetSprite(go, gSpriteCache[scEnemy]);
  1003.             
  1004.             go->kind = objectEnemy;
  1005.             go->screenX = x;
  1006.             go->screenY = y;
  1007.             go->velocityH = kEnemyVelocity;
  1008.             go->velocityV = 0;
  1009.             go->action = EnemyAction;
  1010.             
  1011.             x += w;
  1012.             gNumEnemies++;
  1013.         }
  1014.         
  1015.         y += h;
  1016.     }
  1017. }
  1018.  
  1019. //•    --------------------    AddPoints
  1020.  
  1021. static void
  1022. AddPoints(short x, short y)
  1023. {
  1024. Rect            r;
  1025. GameObjectPtr    go;
  1026.  
  1027.     go = GameObjectAllocate();
  1028.     if (! go)
  1029.         FatalError("Could not allocate player shot.");
  1030.         
  1031.     GameObjectAddToList(&gMiscObjectList, go);
  1032.     
  1033.     SetRect(&r, 0, 0, 640, 440);
  1034.     GameObjectSetBounds(go, &r);
  1035.     GameObjectSetSprite(go, gSpriteCache[scPoints]);
  1036.     
  1037.     go->screenX = x;
  1038.     go->screenY = y;
  1039.     go->velocityH = 0;
  1040.     go->velocityV = kPointsVelocity;
  1041.     go->action = PointsAction;
  1042. }
  1043.  
  1044. //•    --------------------    AddParticles
  1045.  
  1046. void
  1047. AddParticles(short x, short y, ObjectKind kind)
  1048. {
  1049. Rect            r;
  1050. GameObjectPtr    go;
  1051. ParticlesPtr    particles;
  1052. UInt8            color;
  1053.  
  1054.     go = GameObjectAllocate();
  1055.     if (! go)
  1056.         FatalError("Could not allocate player shot.");
  1057.         
  1058.     GameObjectAddToList(&gMiscObjectList, go);
  1059.     
  1060.     SetRect(&r, 0, 0, 640, 400);
  1061.     GameObjectSetBounds(go, &r);
  1062.     go->frame = 0;
  1063.  
  1064.     switch (kind)
  1065.     {
  1066.         case objectEnemyParticles:
  1067.             color = kEnemyParticleColor;
  1068.             break;
  1069.             
  1070.         case objectPlayerParticles:
  1071.             color = kPlayerParticleColor;
  1072.             break;
  1073.             
  1074.         case objectPlasmaParticles:
  1075.             color = kPlasmaParticleColor;
  1076.             break;
  1077.             
  1078.         case objectMissileParticles:
  1079.             color = kMissileParticleColor;
  1080.             break;
  1081.         
  1082.         default:
  1083.             color = 255;
  1084.     }
  1085.     
  1086.     particles = ParticlesAllocate(x, y, color);
  1087.     GameObjectSetParticles(go, particles);
  1088.     go->kind = kind;
  1089.  
  1090.     go->action = ParticleAction;
  1091. }
  1092.  
  1093. #pragma mark -
  1094.  
  1095. //•    --------------------    DisplayFrameRate
  1096.  
  1097. static void
  1098. DisplayFrameRate(void)
  1099. {
  1100. static UInt32    lastTime = 0;
  1101. static UInt32    frames = 0;
  1102.  
  1103. UInt32            elapsedTime;
  1104. Str255            str;
  1105. GrafPtr            oldPort;
  1106. CGrafPtr        underlay;
  1107. GDHandle        device, oldDevice;
  1108. Rect            r;
  1109. UInt32            stringWidth;
  1110.  
  1111.     //•    Initialize the stats at the beginning of each game
  1112.     //•    gFrameRateBaseTime is reset in InitNewGame()
  1113.     if (gFrameRateBaseTime == 0)
  1114.     {
  1115.         gFrameRateBaseTime = TickCount();
  1116.         lastTime = 0L;
  1117.         frames = 0L;
  1118.     }
  1119.  
  1120.     //•    Bump the frame counter
  1121.     frames++;
  1122.     
  1123.     //•    Find the total elapsed time and convert it to seconds
  1124.     elapsedTime = TickCount() - gFrameRateBaseTime;
  1125.  
  1126.     if ( ( elapsedTime - lastTime ) > 120 )
  1127.     {
  1128.         lastTime = elapsedTime;
  1129.         elapsedTime /= 60;
  1130.  
  1131. #if GENERATINGPOWERPC
  1132.         sprintf((char *) str + 1, "FPS:   %0.4ld,   SSp Load:   %ld/%ld", frames / elapsedTime, (gCPULoadMax - gCPUCurrentLoad), gCPULoadMax);
  1133. #else
  1134.         sprintf((char *) str + 1, "FPS:   %0.4ld", frames / elapsedTime);
  1135. #endif
  1136.  
  1137.         str[0] = strlen ((char *) str + 1);
  1138.         stringWidth = StringWidth(str);
  1139.  
  1140.         GraphicsGetUnderlayGrafPort(&underlay, &device);
  1141.  
  1142.         GetPort(&oldPort);
  1143.         oldDevice = GetGDevice();
  1144.         
  1145.         SetPort((GrafPtr) underlay);
  1146.         SetGDevice(device);
  1147.         
  1148.         RGBForeColor(&rgbBlack);
  1149.         SetRect(&r, 5, 420, 5 + stringWidth, 435);
  1150.         PaintRect(&r);
  1151.  
  1152.         MoveTo(5, 435);
  1153.         TextFont(kFontIDGeneva);
  1154.         TextFace(0);
  1155.         TextSize(9);
  1156.         
  1157.         RGBForeColor(&rgbYellow);
  1158.         DrawString(str);
  1159.         RGBForeColor(&rgbBlack);
  1160.         
  1161.         SetPort(oldPort);
  1162.         SetGDevice(oldDevice);
  1163.         
  1164.         GraphicsSetUnderlayRectDirty(&r);
  1165.     }
  1166. }
  1167.  
  1168. //•    --------------------    DisplayGameOver
  1169.  
  1170. static void
  1171. DisplayGameOver(CGrafPtr backBuff)
  1172. {
  1173. GrafPtr        oldPort;
  1174. GDHandle    oldDevice;
  1175. Str255        str = "\pGame Over!";
  1176. UInt16        width;
  1177. UInt16        offset;
  1178.  
  1179.     GetPort(&oldPort);
  1180.     oldDevice = GetGDevice();
  1181.     
  1182.     SetGDevice(gGameGDH);
  1183.     SetPort((GrafPtr) backBuff);
  1184.     
  1185.     TextFont(kFontIDGeneva);
  1186.     TextFace(bold);
  1187.     TextSize(48);
  1188.     
  1189.     //•    Find the width of our string
  1190.     width = StringWidth(str);
  1191.  
  1192.     //•    Find an offset that will center the string in the buffer
  1193.     offset = backBuff->portRect.right - backBuff->portRect.left;
  1194.     offset /= 2;
  1195.     offset -= width / 2;
  1196.     
  1197.     MoveTo(offset, 200);
  1198.     RGBForeColor(&rgbYellow);
  1199.     DrawString(str);
  1200.  
  1201.     MoveTo(offset - 2, 200);
  1202.     RGBForeColor(&rgbBlue);
  1203.     DrawString(str);
  1204.  
  1205.     MoveTo(offset - 4, 200);
  1206.     RGBForeColor(&rgbRed);
  1207.     DrawString(str);
  1208.  
  1209.     RGBForeColor(&rgbBlack);
  1210.     
  1211.     SetGDevice(oldDevice);;
  1212.     SetPort(oldPort);
  1213.  
  1214.     GraphicsSetRectDirty(&backBuff->portRect);
  1215. }
  1216.  
  1217. //•    --------------------    EndGame
  1218.  
  1219. static void
  1220. EndGame(CGrafPtr backBuff)
  1221. {
  1222.     //•    Tell InputSprocket to stop watching our devices so that we can use them
  1223.     //•    for menu command or whatever.
  1224.     ISpSuspend();
  1225.  
  1226.     ShutdownMoviePlayback();
  1227.  
  1228.     //•    Display the "Game Over" banner and pause the graphics system
  1229.     DisplayGameOver(backBuff);
  1230.     GraphicsUpdateScreen();
  1231.     GraphicsPaused();
  1232.  
  1233.     ShowCursor();
  1234.     gGameInProgress = false;
  1235.  
  1236.     //•    Tell network players to shut down
  1237.     if (gNetGame)
  1238.     {
  1239.         Boolean    OKHit;
  1240.         
  1241.         if (gIAmHost)
  1242.             gNetState = kHosting;
  1243.         else
  1244.             gNetState = kJoining;
  1245.  
  1246.         OKHit = WaitForAllPlayers();
  1247.  
  1248.         if (OKHit == false)
  1249.             ShutdownNetworking();
  1250.         else
  1251.         {
  1252.             gNetState = kStarting;
  1253.             SendStartGame();
  1254.             InitNewGame(1);
  1255.         }
  1256.     }    
  1257. }
  1258.