Source/SpaceInvaders.c

//¥ ------------------------------------------------------------------------------------------  ¥
//¥
//¥     Copyright ©1995, Chris De Salvo
//¥     Not to be distributed without expressed consent
//¥
//¥     Written by Chris De Salvo
//¥
//¥ ------------------------------------------------------------------------------------------  ¥
 
//¥ ------------------------------  Includes
 
#include <Fonts.h>
#include <stdio.h>
#include <string.h>
 
#include "ErrorHandler.h"
#include "EventHandler.h"
#include "GameObject.h"
#include "Graphics.h"
#include "ObjectActions.h"
#include "SIResources.h"
#include "SoundHandler.h"
#include "SpaceInvaders.h"
#include "Sprite.h"
#include "NetSprocketSupport.h"
#include "CommonStuff.h"
 
//¥ ------------------------------  Private Definitions
 
//#define RAND(x)                   ((Random() & 0x7FFF) % (x))
 
#define kPlayerVelocity             2L  //¥ Speed at which player moves
#define kPlayerShotVelocity         6L  //¥ Speed at which player shots travel
#define kPointsVelocity             -2L //¥ Vertical speed of the "points" object (negative is up)
#define kEnemyVelocity              5L  //¥ Speed at which enemies move
#define kEnemyShotVelocity          4L  //¥ Speed at which enemy shots drop
 
#define kEnemyOffset                8   //¥ Space between columns of enemies
 
//¥ ------------------------------  Private Types
 
enum
{
    scPlayer,
    scPlayer2,
    scPlayerShot,
    scPoints,
    scEnemy,
    scEnemyShot,
    scNumSprites
};
 
//¥ ------------------------------  Private Variables
 
static SpritePtr    gSpriteCache[scNumSprites]; //¥ Sprite artwork is cached here so that multiple items using
                                                //¥ the same artwork only need one copy.
static long             gScore;
static short            gWave;
static short            gEnemyGas;
static int              gEnemyNewDirection;
static unsigned long    gFrameRateBaseTime;     //¥ Base time for this game to count frame rate
 
float gGreenAccum;
float gRedAccum;
 
extern int gMyRand;
extern int gHisRand;
extern int gMyCallsToRand;
extern int gHisCallsToRand;
 
//¥ ------------------------------  Private Functions
 
static void AddPlayers(void);
static void AddEnemies(void);
static void CollideShotsToEnemies(void);
static void AddPoints(short x, short y);
static void DisplayGameOver(CGrafPtr backBuff);
static void DropEnemies(void);
static void CollideShotsToPlayers(void);
static void AdvanceEnemies(void);
static void DisplayFrameRate(void);
static void EndGame(CGrafPtr backBuff);
static void GetGreenInput(void);
static void GetRedInput(void);
 
//¥ ------------------------------  Public Variables
 
Boolean gGameInProgress = false;
Boolean gTwoPlayers = false;
 
GameObjectPtr   gEnemyList = nil;
GameObjectPtr   gEnemyShotList = nil;
GameObjectPtr   gPlayerList = nil;
GameObjectPtr   gGreenPlayerShotList = nil;
GameObjectPtr   gRedPlayerShotList = nil;
GameObjectPtr   gMiscObjectList = nil;
 
int     gEnemyTask;
int     gEnemiesChangeDirection;
short   gEnemyVelocity;
short   gNumEnemies;
 
unsigned long   gEnemyLevel;
unsigned long   gNumEnemiesProcessed;
 
int         gNumGreenPlayerLives = 0;
int         gNumRedPlayerLives = 0;
 
Boolean         gNetPlay;
extern Boolean  gReceivedInput;
 
//¥ --------------------    InitNewGame
 
static void InitNewRound(short wave)
{
    GameObjectDisposeList(&gEnemyList);
    GameObjectDisposeList(&gEnemyShotList);
    GameObjectDisposeList(&gPlayerList);
    GameObjectDisposeList(&gGreenPlayerShotList);
    GameObjectDisposeList(&gRedPlayerShotList);
    GameObjectDisposeList(&gMiscObjectList);
 
    AddPlayers();
    AddEnemies();
    
    gEnemyTask = kEnemyMovingRight;
    gEnemiesChangeDirection = 0;
    gEnemyVelocity = kEnemyVelocity;
    gEnemyLevel = 1;
    gWave = wave;
}
 
static void NextRound(void)
{
    InitNewRound(gWave + 1);
}
 
 
void
InitNewGame(short wave)
{
    int i;
    
    for (i = 0; i < numInputs; i++)
        ISpElement_Flush(gInputElements[i]);
    
    gGreenAccum = 0;
    gRedAccum = 0;
 
    gGameInProgress = true;
    gScore = 0L;
 
    gFrameRateBaseTime = 0L;
 
    for (i = 0; i < scNumSprites; i++)
        SpriteDispose(&(gSpriteCache[i]));
 
    gSpriteCache[scPlayer] = SpriteLoad(kSPRTPlayer);
    if (! gSpriteCache[scPlayer])
        FatalError("Could not load player tank.");
 
    gSpriteCache[scPlayer2] = SpriteLoad(kSPRTPlayer2);
    if (! gSpriteCache[scPlayer])
        FatalError("Could not load player 2 tank.");
 
    gSpriteCache[scPlayerShot] = SpriteLoad(kSPRTPlayerShot);
    if (! gSpriteCache[scPlayerShot])
        FatalError("Could not load player shot.");
        
    gSpriteCache[scPoints] = SpriteLoad(kSPRTPoints);
    if (! gSpriteCache[scPoints])
        FatalError("Could no load points.");
 
    gSpriteCache[scEnemy] = SpriteLoad(kSPRTEnemy);
    if (! gSpriteCache[scEnemy])
        FatalError("Could no load enemy.");
 
    gSpriteCache[scEnemyShot] = SpriteLoad(kSPRTEnemyShot);
    if (! gSpriteCache[scEnemyShot])
        FatalError("Could no load enemy shot.");
 
 
    gNumGreenPlayerLives = 3;
    
    if (gTwoPlayers)
        gNumRedPlayerLives = 3;
    else
        gNumRedPlayerLives = 0;
    
    GraphicsActive();
 
    ISpResume();
        
    InitNewRound(wave);
}
 
//¥ --------------------    GameLoop
 
void
GameLoop()
{
CGrafPtr            backBuff;
    ISpElementEvent event;
    Boolean         wasEvent;
        
    if (gNetPlay)
    {
        if (gIAmHost)
        {
            GetGreenInput();
            SendInputState(gGameKeys.greenLeft, gGameKeys.greenRight, gGameKeys.greenFire);
            GetRedInput();
        }
        else
        {
            GetRedInput();
            SendInputState(gGameKeys.redLeft, gGameKeys.redRight, gGameKeys.redFire);
            GetGreenInput();
        }   
    }
    else
    {
        GetGreenInput();
        if (gTwoPlayers)
            GetRedInput();
    }
    
 
    //¥ Perform player/missile collission detection
    CollideShotsToEnemies();
    CollideShotsToPlayers();
 
    //¥ If any enemy hit a boundry on the last run, switch directions
    if (gEnemiesChangeDirection)
    {
        DropEnemies();
        gEnemiesChangeDirection = 0;
        gEnemyTask = kEnemyDropping;
    }
 
    gEnemyGas = gWave + 1;
 
    //¥ Get a reference to the back buffer
    DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuff);
 
 
    //¥ Check the abort key
    ISpElement_GetNextEvent(gInputElements[abort], sizeof (event), &event, &wasEvent);
    if (gGotEndGameMessage || (wasEvent && (event.data == kISpButtonDown)))
    {
        if (gNetPlay && !gGotEndGameMessage)
            SendEndGame();
        EndGame(backBuff);
        return;
    }
 
    //¥ Have all game objects perform their actions
    AdvanceEnemies();
    GameObjectListAdvance(gPlayerList);
    GameObjectListAdvance(gEnemyShotList);
    GameObjectListAdvance(gGreenPlayerShotList);
    GameObjectListAdvance(gRedPlayerShotList);
    GameObjectListAdvance(gMiscObjectList);
 
    //¥ Draw all game objects
    GameObjectListDraw(gEnemyList, backBuff);
    GameObjectListDraw(gPlayerList, backBuff);
    GameObjectListDraw(gEnemyShotList, backBuff);
    GameObjectListDraw(gGreenPlayerShotList, backBuff);
    GameObjectListDraw(gRedPlayerShotList, backBuff);
    GameObjectListDraw(gMiscObjectList, backBuff);
 
    //¥ Check for end of wave/game
    if ((gNumGreenPlayerLives < 1) && (gNumRedPlayerLives < 1))
    {
        EndGame(backBuff);
        return;
    }
    
    if (gNumEnemies < 1)
    {
        NextRound();
    }
 
 
    //¥ Update the frame rate display information
    DisplayFrameRate();
 
    //¥ Redraw the entire screen.  This is done last so that if the game over screen was drawn it gets blit for free
    GraphicsUpdateScreen();
 
}
 
static Boolean GetAutoFireButton(ISpElementReference inElement)
{
    OSStatus error;
    UInt32 input;
    Boolean wasEvent;
    Boolean fire = false;
    
    // poll 
    error = ISpElement_GetSimpleState(inElement, &input);
    if (!error && (input == kISpButtonDown)) 
        { fire = true; } 
 
    // but don't miss fast clicks or macros
    do
    {
        ISpElementEvent event;
 
        error = ISpElement_GetNextEvent(inElement, sizeof(ISpElementEvent), &event, &wasEvent);
 
        if (!error && wasEvent && (event.data == kISpButtonDown))
        {
            fire = true; 
            break;
        }
    } while(wasEvent && !error);
    
    // flush the queue
    ISpElement_Flush(inElement);
    
    return fire;
}
 
void GetGreenInput(void)
{
    UInt32          input;
    
    if (!gNetPlay || (gNetPlay && gIAmHost))    // in netplay, the host is the green player, and the joiner the red
    {
        //¥ Check the movement axis
        ISpElement_GetSimpleState(gInputElements[greenMovement], &input);
 
        gGameKeys.greenLeft = false;
        gGameKeys.greenRight = false;
        
        if (input < 0x2FFFFFFF)
            { gGameKeys.greenLeft = true; }
        else if (input > 0xBFFFFFFF)
            { gGameKeys.greenRight =  true; }
        else if (input < 0x5FFFFFFF)
        { 
            gGreenAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x3FFFFFFF;
            if (gGreenAccum < -1) { gGameKeys.greenLeft = true; gGreenAccum += 1; }
        }
        else if (input > 0x9FFFFFFF)
        {
            gGreenAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x3FFFFFFF;
            if (gGreenAccum > 1) { gGameKeys.greenRight = true; gGreenAccum -= 1; }
        }
        
        //¥ Check the fire button
        gGameKeys.greenFire = GetAutoFireButton(gInputElements[greenFire]);
    }
    else if (gNetPlay && !gIAmHost) //wait for player 1's input
    {
        gReceivedInput = false;
        while (!gReceivedInput && !gGotEndGameMessage)
            HandleNetworking();
    }
}
 
void GetRedInput(void)
{
    UInt32          input;
    if (!gNetPlay || (gNetPlay && !gIAmHost))   // in netplay, the host is the green player, and the joiner the red
    {
        //¥ Check the movement axis
        ISpElement_GetSimpleState(gInputElements[redMovement], &input);
 
        gGameKeys.redLeft = false;
        gGameKeys.redRight = false;
        
        if (input < 0x3FFFFFFF)
            { gGameKeys.redLeft = true; }
        else if (input > 0xAFFFFFFF)
            { gGameKeys.redRight =  true; }
        else if (input < 0x5FFFFFFF)
        { 
            gRedAccum += ((float) input - (float) 0x5FFFFFFF) / (float) 0x2FFFFFFF;
            if (gRedAccum < -1) { gGameKeys.redLeft = true; gRedAccum += 1; }
        }
        else if (input > 0x9FFFFFFF)
        {
            gRedAccum += ((float) input - (float) 0x9FFFFFFF) / (float) 0x2FFFFFFF;
            if (gRedAccum > 1) { gGameKeys.redRight = true; gRedAccum -= 1; }
        }
 
        //¥ Check the fire button
        gGameKeys.redFire = GetAutoFireButton(gInputElements[redFire]);
    }
    else if (gNetPlay && gIAmHost)  //wait for player 2's input
    {
        gReceivedInput = false;
        while (!gReceivedInput && !gGotEndGameMessage)
            HandleNetworking();
    }
}
 
//¥ --------------------    AddPlayers
 
static void
AddPlayers(void)
{
Rect            r;
GameObjectPtr   go;
 
    go = GameObjectAllocate();
    if (! go)
        FatalError("Could not allocate player object.");
        
    GameObjectAddToList(&gPlayerList, go);
    
    SetRect(&r, 20, 0, 620, 0);
    GameObjectSetBounds(go, &r);
    GameObjectSetSprite(go, gSpriteCache[scPlayer]);
    
    go->kind = objectGreenPlayer;
    go->screenX = 20;
    go->screenY = 412;
    go->velocityH = kPlayerVelocity;
    go->velocityV = 0;
    go->action = GreenPlayerAction;
 
    //¥ Sloppy way to do this, but hey, I'm in a hurry
    if (gTwoPlayers)
    {
        go = GameObjectAllocate();
        if (! go)
            FatalError("Could not allocate player 2 object.");
            
        GameObjectAddToList(&gPlayerList, go);
        
        SetRect(&r, 20, 0, 620, 0);
        GameObjectSetBounds(go, &r);
        GameObjectSetSprite(go, gSpriteCache[scPlayer2]);
        
        go->kind = objectRedPlayer;
        go->screenX = 640 - 20;
        go->screenY = 412;
        go->velocityH = kPlayerVelocity;
        go->velocityV = 0;
        go->action = RedPlayerAction;
    }
}
 
//¥ --------------------    AddEnemies
 
static void
AddEnemies(void)
{
Rect            r;
GameObjectPtr   go;
int         i, j;
int         w, h;
int         x, x2, y;
 
    gNumEnemies = 0;
 
    //¥ Find dimensions of enemy sprite so we can center the rows to begin with
    w = SpriteWidth(gSpriteCache[scEnemy], 0);
    w += kEnemyOffset;
    h = SpriteHeight(gSpriteCache[scEnemy], 0);
    h += kEnemyOffset;
    
    x2 = w * kNumEnemyColumns;
    x2 = 320 - (x2 / 2);
 
    y = 100;
 
    SetRect(&r, 20, 20, 620, 400);
 
    for (i = 0; i < kNumEnemyRows; i++)
    {
        x = x2;
        
        for (j = 0; j < kNumEnemyColumns; j++)
        {
            go = GameObjectAllocate();
            if (! go)
                FatalError("Could not allocate enemy object.");
            
            GameObjectAddToList(&gEnemyList, go);
            
            GameObjectSetBounds(go, &r);
            GameObjectSetSprite(go, gSpriteCache[scEnemy]);
            
            go->kind = objectEnemy;
            go->screenX = x;
            go->screenY = y;
            go->velocityH = kEnemyVelocity;
            go->velocityV = 0;
            go->action = EnemyAction;
            
            x += w;
            gNumEnemies++;
        }
        
        y += h;
    }
}
 
//¥ --------------------    PlayerShoot
 
void
PlayerShoot(GameObjectPtr whichPlayer)
{
Rect            r;
GameObjectPtr   go;
GameObjectPtr   shotList;
 
 
    if (whichPlayer->kind == objectGreenPlayer)
    {
        if (gGreenPlayerShotList)
            return;
        else
            shotList = gGreenPlayerShotList;
    }
    else
    {
        if (gRedPlayerShotList)
            return;
        else
            shotList = gRedPlayerShotList;
    }
 
    SoundHandlerPlay(soundPlayerFire, whichPlayer->screenX, whichPlayer->screenY);
 
    go = GameObjectAllocate();
    if (! go)
        FatalError("Could not allocate player shot.");
        
    GameObjectAddToList(&shotList, go);
    
    SetRect(&r, 20, 0, 620, 0);
    GameObjectSetBounds(go, &r);
    GameObjectSetSprite(go, gSpriteCache[scPlayerShot]);
    
    go->screenX = whichPlayer->screenX;
    go->screenY = whichPlayer->screenY - SpriteHeight(whichPlayer->sprite, whichPlayer->frame) - 1;
    go->velocityH = 0;
    go->velocityV = kPlayerShotVelocity;
    go->action = PlayerShotAction;
    
    if (whichPlayer->kind == objectRedPlayer)
    {
        go->kind = objectRedPlayerShot;
        if (! gRedPlayerShotList)
            gRedPlayerShotList = shotList;
    }
    else
    {
        go->kind = objectGreenPlayerShot;
        if (! gGreenPlayerShotList)
            gGreenPlayerShotList = shotList;
    }
}
 
//¥ --------------------    CollideShotsToEnemies
 
static void
CollideShotsToEnemies()
{
GameObjectPtr   shot;
GameObjectPtr   target;
 
    if (! gEnemyList)
        return;
        
    //¥ Iterate over the green player shots
    if (gGreenPlayerShotList)
    {
        for (shot = gGreenPlayerShotList; shot; shot = shot->next)
        {
            //¥ Iterate over the enemies
            for (target = gEnemyList; target; target = target->next)
            {
            Rect    u;  //¥ Union of shot and enemy screen rects
            
                SectRect(&shot->screenRect, &target->screenRect, &u);
                if (! EmptyRect(&u))
                {
                    SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
                    
                    shot->action = PlayerShotDestroy;
                    target->action = EnemyDestroy;
                    AddPoints(target->screenX, target->screenY);
                }
            }
        }
    }
    
    //¥ Iterate over the red player shots
    if (gRedPlayerShotList)
    {
        for (shot = gRedPlayerShotList; shot; shot = shot->next)
        {
            //¥ Iterate over the enemies
            for (target = gEnemyList; target; target = target->next)
            {
            Rect    u;  //¥ Union of shot and enemy screen rects
            
                SectRect(&shot->screenRect, &target->screenRect, &u);
                if (! EmptyRect(&u))
                {
                    SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY);
                    
                    shot->action = PlayerShotDestroy;
                    target->action = EnemyDestroy;
                    AddPoints(target->screenX, target->screenY);
                }
            }
        }
    }
}
 
//¥ --------------------    CollideShotsToPlayers
 
static void
CollideShotsToPlayers(void)
{
GameObjectPtr   shot;
GameObjectPtr   target;
GameObjectPtr   next;
 
    if (! gEnemyShotList || ! gPlayerList)
        return;
 
    target = gPlayerList;
    next = target->next;
    while (target != nil)
    {
        if (target->refCon > 0)
            return;
 
        //¥ Iterate over the enemy shots
        for (shot = gEnemyShotList; shot; shot = shot->next)
        {
        Rect    u;  //¥ Union of shot and enemy screen rects
            
            SectRect(&shot->screenRect, &target->screenRect, &u);
            if (! EmptyRect(&u))
            {
                SoundHandlerPlay(soundPlayerHit,target->screenX, target->screenY);
 
                shot->action = EnemyShotDestroy;
                target->action = PlayerDestroy;
                
                if (target->kind == objectGreenPlayer)
                {
                    gNumGreenPlayerLives--;
                    if (gNumGreenPlayerLives < 1)
                        GameObjectRemoveFromList(&gPlayerList, target);
                }
                else
                {
                    gNumRedPlayerLives--;
                    if (gNumRedPlayerLives < 1)
                        GameObjectRemoveFromList(&gPlayerList, target);
                }
            }
        }
        
        target = next;
        next = target->next;
    }
}
 
//¥ --------------------    AddPoints
 
static void
AddPoints(short x, short y)
{
Rect            r;
GameObjectPtr   go;
 
    gScore += 100L;
 
    go = GameObjectAllocate();
    if (! go)
        FatalError("Could not allocate player shot.");
        
    GameObjectAddToList(&gMiscObjectList, go);
    
    SetRect(&r, 0, 0, 640, 440);
    GameObjectSetBounds(go, &r);
    GameObjectSetSprite(go, gSpriteCache[scPoints]);
    
    go->screenX = x;
    go->screenY = y;
    go->velocityH = 0;
    go->velocityV = kPointsVelocity;
    go->action = PointsAction;
}
 
//¥ --------------------    DisplayGameOver
 
static void
DisplayGameOver(CGrafPtr backBuff)
{
GrafPtr oldPort;
Str255  str = "\pGame Over!";
short   width;
short   offset;
 
    GetPort(&oldPort);
    SetPort((GrafPtr) backBuff);
    
    TextFont(geneva);
    TextFace(bold);
    TextSize(48);
    
    //¥ Find the width of our string
    width = StringWidth(str);
 
    //¥ Find an offset that will center the string in the buffer
    offset = backBuff->portRect.right - backBuff->portRect.left;
    offset /= 2;
    offset -= width / 2;
    
    MoveTo(offset, 200);
    RGBForeColor(&rgbYellow);
    DrawString(str);
 
    MoveTo(offset - 2, 200);
    RGBForeColor(&rgbBlue);
    DrawString(str);
 
    MoveTo(offset - 4, 200);
    RGBForeColor(&rgbRed);
    DrawString(str);
 
    RGBForeColor(&rgbBlack);
    
    SetPort(oldPort);
    GraphicsSetRectDirty(&backBuff->portRect);
}
 
//¥ --------------------    DropEnemies
 
static void
DropEnemies()
{
GameObjectPtr   current, next;
 
    gEnemyNewDirection = gEnemiesChangeDirection;
 
    do
    {
        current = gEnemyList;
        gNumEnemiesProcessed = 0;
        
        do
        {
            next = current->next;
            current->action(current);
            current = next;
        } while (next);
    } while (gNumEnemiesProcessed);
 
    gEnemyLevel++;
}
 
//¥ --------------------    EnemyShoot
 
void
EnemyShoot(GameObjectPtr whichEnemy)
{
Rect            r;
GameObjectPtr   go;
 
    go = GameObjectAllocate();
    if (! go)
        FatalError("Could not allocate enemy shot.");
        
    SoundHandlerPlay(soundEnemyFire, whichEnemy->screenX, whichEnemy->screenY);
 
    GameObjectAddToList(&gEnemyShotList, go);
    
    SetRect(&r, 20, 0, 620, 412);
    GameObjectSetBounds(go, &r);
    GameObjectSetSprite(go, gSpriteCache[scEnemyShot]);
    
    go->screenX = whichEnemy->screenX;
    go->screenY = whichEnemy->screenY + 1;
    go->velocityH = 0;
    go->velocityV = kEnemyShotVelocity;
    go->action = EnemyShotAction;
}
 
//¥ --------------------    AdvanceEnemies
 
static void
AdvanceEnemies()
{
GameObjectPtr   current, next;
unsigned long   oldProcessed;
 
    do
    {
        gNumEnemiesProcessed = 0;
        current = gEnemyList;
        
        do
        {
            oldProcessed = gNumEnemiesProcessed;
            next = current->next;
            current->action(current);
            
            //¥ If we moved an alien, use up some alien gas
            if (oldProcessed != gNumEnemiesProcessed)
                gEnemyGas--;
                
            current = next;
        } while (next && gEnemyGas);
        
        //¥ If no enemies were processed then they're all at the same level, so move on
        if (gNumEnemiesProcessed == 0)
        {
            gEnemyLevel++;
            
            if (gEnemyTask == kEnemyDropping)
                gEnemyTask = gEnemyNewDirection;
        }
 
    } while (gEnemyGas && (gNumEnemies > 0));
}
 
//¥ --------------------    DisplayFrameRate
 
static void
DisplayFrameRate(void)
{
static unsigned long    lastTime = 0;
static unsigned long    frames = 0;
unsigned long       elapsedTime;
Str255          str;
GrafPtr         oldPort;
CGrafPtr            underlay;
GDHandle            device, oldDevice;
Rect                r;
 
    //¥ Initialize the stats at the beginning of each game
    //¥ gFrameRateBaseTime is reset in InitNewGame()
    if (gFrameRateBaseTime == 0)
    {
        gFrameRateBaseTime = TickCount();
        lastTime = 0L;
        frames = 0L;
    }
 
    //¥ Bump the frame counter
    frames++;
    
    //¥ Find the total elapsed time and convert it to seconds
    elapsedTime = TickCount() - gFrameRateBaseTime;
 
    if ( ( elapsedTime - lastTime ) > 120 )
    {
        lastTime = elapsedTime;
        elapsedTime /= 60;
        sprintf((char *) str + 1, "FPS:   %0.4ld", frames / elapsedTime);
        str[0] = strlen ((char *) str + 1);
 
        GraphicsGetUnderlayGrafPort(&underlay, &device);
 
        GetPort(&oldPort);
        oldDevice = GetGDevice();
        
        SetPort((GrafPtr) underlay);
        SetGDevice(device);
        
        RGBForeColor(&rgbBlack);
        SetRect(&r, 5, 420, 90, 435);
        PaintRect(&r);
 
        MoveTo(5, 435);
        TextFont(systemFont);
        TextFace(bold);
        TextSize(12);
        
        RGBForeColor(&rgbYellow);
        DrawString(str);
        RGBForeColor(&rgbBlack);
        
        SetPort(oldPort);
        SetGDevice(oldDevice);
        
        GraphicsSetUnderlayRectDirty(&r);
    }
}
 
//¥ --------------------    EndGame
 
static void
EndGame(CGrafPtr backBuff)
{
    Boolean OKHit;
    
    ISpSuspend();
    DisplayGameOver(backBuff);
    GraphicsUpdateScreen();
    GraphicsPaused();
    ShowCursor();
    gGameInProgress = false;
    if (gNetGame)
    {
        if (gIAmHost)
            gNetState = kHosting;
        else
            gNetState = kJoining;
        OKHit = WaitForAllPlayers();
        if (OKHit == false)
            ShutdownNetworking();
        else
        {
            gNetState = kStarting;
            SendStartGame();
            InitNewGame(1);
        }
    }   
    
}