home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Games / SprocketInvaders / Source / SprocketInvaders.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  30.8 KB  |  1,413 lines  |  [TEXT/MPS ]

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