Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Source/SprocketInvaders.c
/* |
File: SprocketInvaders.c |
Contains: xxx put contents here xxx |
Version: xxx put version here xxx |
Copyright: © 1998-1999 by Apple Computer, Inc., all rights reserved. |
File Ownership: |
DRI: xxx put dri here xxx |
Other Contact: xxx put other contact here xxx |
Technology: xxx put technology here xxx |
Writers: |
(cjd) Chris De Salvo |
(cjd) Chris DeSalvo |
(BWS) Brent Schorsch |
Change History (most recent first): |
<SP20> 3/3/99 cjd Change player shot/CD controls code so that shots go through CD |
controls. |
<SP19> 1/29/99 cjd Added code for new CD controller sprites. |
<SP18> 1/21/99 cjd Removing 68K build code |
<17> 7/2/98 BWS fix 68k build (no SSp toggle) |
<16> 7/1/98 cjd Made it so that you can shoot enemy shots, added optimization |
for collission of player shots and enemies, added new FPS |
display for SSp usage. |
<15> 6/12/98 BWS InputSprocket 68k now used |
*/ |
//¥ ------------------------------------------------------------------------------------------ ¥ |
//¥ |
//¥ Copyright © 1996 Apple Computer, Inc., All Rights Reserved |
//¥ |
//¥ |
//¥ You may incorporate this sample code into your applications without |
//¥ restriction, though the sample code has been provided "AS IS" and the |
//¥ responsibility for its operation is 100% yours. However, what you are |
//¥ not permitted to do is to redistribute the source as "DSC Sample Code" |
//¥ after having made changes. If you're going to re-distribute the source, |
//¥ we require that you make it clear in the source that the code was |
//¥ descended from Apple Sample Code, but that you've made changes. |
//¥ |
//¥ Authors: |
//¥ Chris De Salvo |
//¥ Jamie Osborne |
//¥ Michael Evans |
//¥ Tim Carroll |
//¥ |
//¥ ------------------------------------------------------------------------------------------ ¥ |
//¥ ------------------------------ Includes |
#include <Fonts.h> |
#include <stdio.h> |
#include <string.h> |
#include "ErrorHandler.h" |
#include "EventHandler.h" |
#include "GameObject.h" |
#include "Graphics.h" |
#include "MoviePlayback.h" |
#include "ObjectActions.h" |
#include "CD_Utils.h" |
#include "SIResources.h" |
#include "SoundHandler.h" |
#include "SprocketInvaders.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 10L //¥ 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 |
#define kEnemyParticleColor 200 |
#define kPlayerParticleColor 192 |
#define kPlasmaParticleColor 208 |
#define kMissileParticleColor 216 |
//¥ ------------------------------ Private Types |
enum |
{ |
scPlayer, |
scPlayer2, |
scPlayerShot, |
scPoints, |
scEnemy, |
scEnemyShot, |
scPlayButton, |
scStopButton, |
scPrevButton, |
scSkipButton, |
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 UInt32 gWave; //¥ Current level number |
static UInt32 gEnemyGas; //¥ Number of enemies that get updated per game loop |
static SInt32 gEnemyNewDirection; //¥ If enemies changed directions, this is the new direction |
static UInt32 gFrameRateBaseTime; //¥ Base time for this game to count frame rate |
static float gGreenAccum; |
static float gRedAccum; |
//¥ ------------------------------ Private Functions |
static void AddPlayers(void); |
static void AddEnemies(void); |
static void CollideShotsToEnemies(void); |
static void CollideShotsToPlayers(void); |
static void CollideShotsToShots(void); |
static void AddCDControls(void); |
static void AddPoints(short x, short y); |
static void DisplayGameOver(CGrafPtr backBuff); |
static void DropEnemies(void); |
static void AdvanceEnemies(void); |
static void DisplayFrameRate(void); |
static void EndGame(CGrafPtr backBuff); |
static void GetGreenInput(void); |
static void GetRedInput(void); |
static Boolean WasButtonHit (ISpElementReference inElement); |
static void BuildEnemyRect(void); |
static void DoCDAction(UInt32 inAction); |
static void CollideShotsToCDControls(void); |
//¥ ------------------------------ Public Variables |
Boolean gGameInProgress = false; |
Boolean gTwoPlayers = false; |
GameObjectPtr gEnemyList = nil; //¥ List of enemies |
GameObjectPtr gEnemyShotList = nil; //¥ List of enemy shots |
GameObjectPtr gPlayerList = nil; //¥ List of players |
GameObjectPtr gGreenPlayerShotList = nil; //¥ List of green player shots |
GameObjectPtr gRedPlayerShotList = nil; //¥ List of red player shots |
GameObjectPtr gMiscObjectList = nil; //¥ List of misc items (score sprites, etc) |
Rect gEnemyRect; //¥ Rect enclosing all enemies |
UInt32 gEnemyTask; |
SInt32 gEnemiesChangeDirection; |
SInt32 gEnemyVelocity; |
UInt32 gNumEnemies; |
UInt32 gEnemyLevel; |
UInt32 gNumEnemiesProcessed; |
UInt32 gNumGreenPlayerLives = 0; |
UInt32 gNumRedPlayerLives = 0; |
Boolean gNetPlay; |
//¥ -------------------- InitNewRound |
static void |
InitNewRound(UInt32 wave) |
{ |
SInt16 numCDTracks; |
//¥ Flush out our object lists |
GameObjectDisposeList(&gEnemyList); |
GameObjectDisposeList(&gEnemyShotList); |
GameObjectDisposeList(&gPlayerList); |
GameObjectDisposeList(&gGreenPlayerShotList); |
GameObjectDisposeList(&gRedPlayerShotList); |
GameObjectDisposeList(&gMiscObjectList); |
//¥ Add our initial objects |
AddPlayers(); |
AddEnemies(); |
if (true == gCDAudio) |
AddCDControls(); |
gEnemyTask = kEnemyMovingRight; |
gEnemiesChangeDirection = 0; |
gEnemyVelocity = kEnemyVelocity; |
gEnemyLevel = 1; |
gWave = wave; |
if (true == gCDAudio) |
{ |
//¥ Pick a CD audio track and start playback if CD audio was chosen |
numCDTracks = CD_GetNumTracks(); |
if (numCDTracks > 0) |
{ |
SInt16 trackNum; |
if (wave > numCDTracks) |
{ |
trackNum = (wave % numCDTracks) + 1; |
} |
else |
{ |
trackNum = wave; |
} |
//¥ Unnecessary error checking |
if (trackNum < 1) |
trackNum = 1; |
if (trackNum > numCDTracks) |
trackNum = numCDTracks; |
CD_PlayAudioTrack(trackNum, playmodeStereo, true); |
} |
} |
SetRect(&gEnemyRect, 0, 0, 0, 0); |
} |
//¥ -------------------- NextRound |
static void |
NextRound(void) |
{ |
InitNewRound(gWave + 1); |
} |
//¥ -------------------- InitNewGame |
void |
InitNewGame(UInt32 wave) |
{ |
UInt32 i; |
//¥ Start up the background movie if one was chosen |
PlaybackMovie(); |
for (i = 0; i < numInputs; i++) |
ISpElement_Flush(gInputElements[i]); |
gGreenAccum = 0; |
gRedAccum = 0; |
gGameInProgress = true; |
gFrameRateBaseTime = 0L; |
//¥ Flush out the sprite cache and load in the new sprites |
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 not load points."); |
gSpriteCache[scEnemy] = SpriteLoad(kSPRTEnemy); |
if (! gSpriteCache[scEnemy]) |
FatalError("Could not load enemy."); |
gSpriteCache[scEnemyShot] = SpriteLoad(kSPRTEnemyShot); |
if (! gSpriteCache[scEnemyShot]) |
FatalError("Could not load enemy shot."); |
gSpriteCache[scPlayButton] = SpriteLoad(kSPRTPlayButton); |
if (! gSpriteCache[scPlayButton]) |
FatalError("Could not load play button."); |
gSpriteCache[scStopButton] = SpriteLoad(kSPRTStopButton); |
if (! gSpriteCache[scStopButton]) |
FatalError("Could not load stop button."); |
gSpriteCache[scPrevButton] = SpriteLoad(kSPRTPrevButton); |
if (! gSpriteCache[scPrevButton]) |
FatalError("Could not load prev button."); |
gSpriteCache[scSkipButton] = SpriteLoad(kSPRTSkipButton); |
if (! gSpriteCache[scSkipButton]) |
FatalError("Could not load skip button."); |
gNumGreenPlayerLives = 3; |
if (gTwoPlayers) |
gNumRedPlayerLives = 3; |
else |
gNumRedPlayerLives = 0; |
//¥ Activate our graphics context and tell InputSprocket to start |
//¥ watching our controllers |
GraphicsActive(); |
ISpResume(); |
InitNewRound(wave); |
} |
#pragma mark - |
//¥ -------------------- GameLoop |
void |
GameLoop(void) |
{ |
CGrafPtr backBuff; |
ISpElementEvent event; |
Boolean wasEvent; |
if (WasButtonHit(gInputElements[soundToggle])) |
gSoundEffects = !gSoundEffects; |
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(); |
} |
//¥ Load in the current SoundSprocket throttle value |
ISpElement_GetSimpleState(gInputElements[soundSprocketThrottle], &gCPUCurrentLoad); |
if (0 == gCPULoadModifier) |
gCPUCurrentLoad = 0; |
else |
gCPUCurrentLoad /= gCPULoadModifier; |
//¥ Perform player/missile collission detection |
CollideShotsToCDControls(); |
CollideShotsToEnemies(); |
CollideShotsToShots(); |
CollideShotsToPlayers(); |
//¥ If any enemy hit a boundry on the last run, switch directions |
if (gEnemiesChangeDirection) |
{ |
DropEnemies(); |
gEnemiesChangeDirection = 0; |
gEnemyTask = kEnemyDropping; |
} |
//¥ gEnemyGas determines how many aliens are processed each game loop |
//¥ This gives the staggered updating on the screen. It also speeds up the |
//¥ alien movement and shooting speed as the game progresses. |
gEnemyGas = gWave + 1; |
ServiceMoviePlayback(); |
//¥ Get a reference to the back buffer so we can draw into it |
DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &backBuff); |
//¥ Check the abort key in case the user wishes to quit out early |
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); |
//¥ Build a master rect of all the enemies to help optimize hit testing |
//¥ the next time through the loop. |
BuildEnemyRect(); |
//¥ Check for end of wave/game |
if ((gNumGreenPlayerLives < 1) && (gNumRedPlayerLives < 1)) |
{ |
EndGame(backBuff); |
return; |
} |
//¥ If we just killed the last enemy then start the next round |
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(); |
} |
//¥ -------------------- GetAutoFireButton |
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); |
} |
//¥ -------------------- WasButtonHit |
static Boolean |
WasButtonHit (ISpElementReference inElement) |
{ |
OSStatus error; |
Boolean wasEvent; |
Boolean down = false; |
do |
{ |
ISpElementEvent event; |
error = ISpElement_GetNextEvent(inElement, sizeof(ISpElementEvent), &event, &wasEvent); |
if (! error && wasEvent && (event.data == kISpButtonDown)) |
{ |
down = true; |
break; |
} |
} while (wasEvent && !error); |
return (down); |
} |
//¥ -------------------- GetGreenInput |
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(); |
} |
} |
//¥ -------------------- GetRedInput |
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(); |
} |
} |
//¥ -------------------- 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->objectData.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; |
} |
} |
//¥ -------------------- AdvanceEnemies |
static void |
AdvanceEnemies(void) |
{ |
GameObjectPtr current, next; |
UInt32 oldProcessed; |
//¥ This set of loops makes sure that gEnemyGas worth |
//¥ of aliens get processed this loop. If gEnemyGas is set to |
//¥ 10, and there are only 5 aliens left then they all update twice |
//¥ for a total of ten updates. This is how we take care us speeeding |
//¥ the aliens up as fewer and fewer of them are present. |
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)); |
} |
//¥ -------------------- DropEnemies |
static void |
DropEnemies(void) |
{ |
GameObjectPtr current, next; |
//¥ One of the enemies hit the edge of their boundary so we need |
//¥ to draw them all one row. |
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; |
//¥ Create a game object for the shot |
go = GameObjectAllocate(); |
if (! go) |
FatalError("Could not allocate enemy shot."); |
//¥ Play the alien shooting sound effect |
SoundHandlerPlay(soundEnemyFire, whichEnemy->screenX, whichEnemy->screenY); |
//¥ Add the shot to the enemy shot list |
GameObjectAddToList(&gEnemyShotList, go); |
//¥ Set the attributes for this object |
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; |
} |
//¥ -------------------- CollideShotsToCDControls |
static void |
CollideShotsToCDControls(void) |
{ |
GameObjectPtr shot; |
GameObjectPtr target; |
if (false == gCDAudio) |
return; |
if (nil == gMiscObjectList) |
return; |
//¥ Iterate over the green player shots |
if (gGreenPlayerShotList) |
{ |
for (shot = gGreenPlayerShotList; shot; shot = shot->next) |
{ |
//¥ Iterate over the controls |
for (target = gMiscObjectList; target; target = target->next) |
{ |
if (target->refCon < scPlayButton || target->refCon > scSkipButton) |
continue; |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
DoCDAction(target->refCon); |
} |
} |
} |
//¥ Iterate over the red player shots |
if (gRedPlayerShotList) |
{ |
for (shot = gRedPlayerShotList; shot; shot = shot->next) |
{ |
//¥ Iterate over the controls |
for (target = gMiscObjectList; target; target = target->next) |
{ |
if (target->refCon < scPlayButton || target->refCon > scSkipButton) |
continue; |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
DoCDAction(target->refCon); |
} |
} |
} |
} |
//¥ -------------------- CollideShotsToEnemies |
static void |
CollideShotsToEnemies(void) |
{ |
GameObjectPtr shot; |
GameObjectPtr target; |
Rect dummyRect; |
if (! gEnemyList) |
return; |
//¥ Iterate over the green player shots |
if (gGreenPlayerShotList) |
{ |
for (shot = gGreenPlayerShotList; shot; shot = shot->next) |
{ |
//¥ Quick easy check to see if the shot is even within the block of enemies |
if (false == SectRect(&shot->screenRect, &gEnemyRect, &dummyRect)) |
continue; |
//¥ Iterate over the enemies |
for (target = gEnemyList; target; target = target->next) |
{ |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
{ |
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) |
{ |
//¥ Quick easy check to see if the shot is even within the block of enemies |
if (false == SectRect(&shot->screenRect, &gEnemyRect, &dummyRect)) |
continue; |
//¥ Iterate over the enemies |
for (target = gEnemyList; target; target = target->next) |
{ |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
{ |
SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY); |
shot->action = PlayerShotDestroy; |
target->action = EnemyDestroy; |
AddPoints(target->screenX, target->screenY); |
} |
} |
} |
} |
} |
//¥ -------------------- CollideShotsToShots |
static void |
CollideShotsToShots(void) |
{ |
GameObjectPtr shot; |
GameObjectPtr target; |
if (nil == gEnemyShotList) |
return; |
//¥ Iterate over the green player shots |
if (gGreenPlayerShotList) |
{ |
for (shot = gGreenPlayerShotList; shot; shot = shot->next) |
{ |
//¥ Iterate over the enemies |
for (target = gEnemyShotList; target; target = target->next) |
{ |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
{ |
shot->action = PlayerShotDestroy; |
target->action = EnemyShotDestroy; |
} |
} |
} |
} |
//¥ Iterate over the red player shots |
if (gRedPlayerShotList) |
{ |
for (shot = gRedPlayerShotList; shot; shot = shot->next) |
{ |
//¥ Iterate over the enemies |
for (target = gEnemyShotList; target; target = target->next) |
{ |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
{ |
SoundHandlerPlay(soundEnemyHit, target->screenX, target->screenY); |
shot->action = PlayerShotDestroy; |
target->action = EnemyShotDestroy; |
} |
} |
} |
} |
} |
//¥ -------------------- 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) |
{ |
if (IntersectRects(&shot->screenRect, &target->screenRect)) |
{ |
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; |
} |
} |
//¥ -------------------- BuildEnemyRect |
static void |
BuildEnemyRect(void) |
{ |
Rect workingRect = { 0, 0, 0, 0 }; |
GameObjectPtr currentEnemy; |
if (nil != gEnemyList) |
currentEnemy = gEnemyList; |
else |
return; |
workingRect = currentEnemy->screenRect; |
do |
{ |
UnionRect(&workingRect, ¤tEnemy->screenRect, &workingRect); |
currentEnemy = currentEnemy->next; |
} while (nil != currentEnemy); |
gEnemyRect = workingRect; |
} |
#pragma mark - |
//¥ -------------------- 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; |
UInt32 i, j; |
UInt32 w, h; |
UInt32 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; |
} |
} |
//¥ -------------------- AddCDControls |
static void |
AddCDControls(void) |
{ |
Rect r; |
GameObjectPtr go; |
UInt32 i; |
for (i = scPlayButton; i <= scSkipButton; i++) |
{ |
go = GameObjectAllocate(); |
if (! go) |
FatalError("Could not allocate CD control."); |
GameObjectAddToList(&gMiscObjectList, go); |
SetRect(&r, 0, 0, 640, 400); |
GameObjectSetBounds(go, &r); |
GameObjectSetSprite(go, gSpriteCache[i]); |
go->screenX = RAND(640); |
go->screenY = RAND(240); |
go->velocityH = RAND(2) + 1; |
go->velocityV = RAND(3) + 1; |
go->refCon = i; |
go->action = ObjectBounce; |
} |
} |
//¥ -------------------- AddPoints |
static void |
AddPoints(short x, short y) |
{ |
Rect r; |
GameObjectPtr go; |
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; |
} |
//¥ -------------------- AddParticles |
void |
AddParticles(short x, short y, ObjectKind kind) |
{ |
Rect r; |
GameObjectPtr go; |
ParticlesPtr particles; |
UInt8 color; |
go = GameObjectAllocate(); |
if (! go) |
FatalError("Could not allocate player shot."); |
GameObjectAddToList(&gMiscObjectList, go); |
SetRect(&r, 0, 0, 640, 400); |
GameObjectSetBounds(go, &r); |
go->frame = 0; |
switch (kind) |
{ |
case objectEnemyParticles: |
color = kEnemyParticleColor; |
break; |
case objectPlayerParticles: |
color = kPlayerParticleColor; |
break; |
case objectPlasmaParticles: |
color = kPlasmaParticleColor; |
break; |
case objectMissileParticles: |
color = kMissileParticleColor; |
break; |
default: |
color = 255; |
} |
particles = ParticlesAllocate(x, y, color); |
GameObjectSetParticles(go, particles); |
go->kind = kind; |
go->action = ParticleAction; |
} |
#pragma mark - |
//¥ -------------------- DisplayFrameRate |
static void |
DisplayFrameRate(void) |
{ |
static UInt32 lastTime = 0; |
static UInt32 frames = 0; |
UInt32 elapsedTime; |
Str255 str; |
GrafPtr oldPort; |
CGrafPtr underlay; |
GDHandle device, oldDevice; |
Rect r; |
UInt32 stringWidth; |
//¥ 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, SSp Load: %ld/%ld", frames / elapsedTime, (gCPULoadMax - gCPUCurrentLoad), gCPULoadMax); |
str[0] = strlen ((char *) str + 1); |
stringWidth = StringWidth(str); |
GraphicsGetUnderlayGrafPort(&underlay, &device); |
GetPort(&oldPort); |
oldDevice = GetGDevice(); |
SetPort((GrafPtr) underlay); |
SetGDevice(device); |
RGBForeColor(&rgbBlack); |
SetRect(&r, 5, 420, 5 + stringWidth, 435); |
PaintRect(&r); |
MoveTo(5, 435); |
TextFont(kFontIDGeneva); |
TextFace(0); |
TextSize(9); |
RGBForeColor(&rgbYellow); |
DrawString(str); |
RGBForeColor(&rgbBlack); |
SetPort(oldPort); |
SetGDevice(oldDevice); |
GraphicsSetUnderlayRectDirty(&r); |
} |
} |
//¥ -------------------- DisplayGameOver |
static void |
DisplayGameOver(CGrafPtr backBuff) |
{ |
GrafPtr oldPort; |
GDHandle oldDevice; |
Str255 str = "\pGame Over!"; |
UInt16 width; |
UInt16 offset; |
GetPort(&oldPort); |
oldDevice = GetGDevice(); |
SetGDevice(gGameGDH); |
SetPort((GrafPtr) backBuff); |
TextFont(kFontIDGeneva); |
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); |
SetGDevice(oldDevice);; |
SetPort(oldPort); |
GraphicsSetRectDirty(&backBuff->portRect); |
} |
//¥ -------------------- EndGame |
static void |
EndGame(CGrafPtr backBuff) |
{ |
//¥ Tell InputSprocket to stop watching our devices so that we can use them |
//¥ for menu command or whatever. |
ISpSuspend(); |
ShutdownMoviePlayback(); |
//¥ Display the "Game Over" banner and pause the graphics system |
DisplayGameOver(backBuff); |
GraphicsUpdateScreen(); |
GraphicsPaused(); |
ShowCursor(); |
gGameInProgress = false; |
//¥ Tell network players to shut down |
if (gNetGame) |
{ |
Boolean OKHit; |
if (gIAmHost) |
gNetState = kHosting; |
else |
gNetState = kJoining; |
OKHit = WaitForAllPlayers(); |
if (OKHit == false) |
ShutdownNetworking(); |
else |
{ |
gNetState = kStarting; |
SendStartGame(); |
InitNewGame(1); |
} |
} |
} |
//¥ -------------------- DoCDAction |
static void |
DoCDAction(UInt32 inAction) |
{ |
SInt16 currentTrack; |
UInt16 numTracks; |
OSStatus theError; |
UInt16 trackToPlay = 0; |
if (false == gCDAudio) |
return; |
numTracks = CD_GetNumTracks(); |
theError = CD_GetCurrentAudioTrack(¤tTrack); |
if (noErr != theError) |
return; |
switch (inAction) |
{ |
case scPlayButton: |
trackToPlay = 1; |
break; |
case scStopButton: |
CD_StopAudioTrack(true); |
break; |
case scPrevButton: |
if (currentTrack == 1) |
trackToPlay = numTracks; |
else |
trackToPlay = currentTrack - 1; |
break; |
case scSkipButton: |
if (currentTrack == numTracks) |
trackToPlay = 1; |
else |
trackToPlay = currentTrack + 1; |
break; |
} |
if (0 != trackToPlay) |
CD_PlayAudioTrack(trackToPlay, playmodeStereo, true); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14