1 line
43 KiB
C
Executable File
1 line
43 KiB
C
Executable File
|
|
//============================================================================
|
|
//----------------------------------------------------------------------------
|
|
// Play.c
|
|
//----------------------------------------------------------------------------
|
|
//============================================================================
|
|
|
|
// This (rather large) file handles all player routines while a game is inÉ
|
|
// progress. It gets the player's input, moves the player, tests for collisionsÉ
|
|
// and generally handles the "main game loop". Enemies and actually drawingÉ
|
|
// the graphics to the screen are handled in other files.
|
|
|
|
#include "Externs.h"
|
|
|
|
|
|
#define kFlapImpulse 48
|
|
#define kGlideImpulse 12
|
|
#define kAirResistance 2
|
|
#define kMaxHVelocity 192
|
|
#define kMaxVVelocity 512
|
|
#define kNumLightningStrikes 5
|
|
|
|
|
|
void SetUpLevel (void);
|
|
void ResetPlayer (Boolean);
|
|
void OffAMortal (void);
|
|
void DoCommandKey (void);
|
|
void GetPlayerInput (void);
|
|
void HandlePlayerIdle (void);
|
|
void HandlePlayerFlying (void);
|
|
void HandlePlayerWalking (void);
|
|
void HandlePlayerSinking (void);
|
|
void HandlePlayerFalling (void);
|
|
void HandlePlayerBones (void);
|
|
void MovePlayer (void);
|
|
void CheckTouchDownCollision (void);
|
|
void CheckPlatformCollision (void);
|
|
void KeepPlayerOnPlatform (void);
|
|
void CheckLavaRoofCollision (void);
|
|
void SetAndCheckPlayerDest (void);
|
|
void HandleLightning (void);
|
|
void FinishLightning (void);
|
|
void HandleCountDownTimer (void);
|
|
void CheckHighScore (void);
|
|
|
|
|
|
playerType thePlayer;
|
|
enemyType theEnemies[kMaxEnemies];
|
|
KeyMap theKeys;
|
|
Rect platformRects[6], touchDownRects[6], enemyRects[24];
|
|
Rect enemyInitRects[5];
|
|
long theScore, wasTensOfThousands;
|
|
short numLedges, beginOnLevel, levelOn, livesLeft, lightH, lightV;
|
|
short lightningCount, numEnemies, countDownTimer;
|
|
Boolean playing, pausing, flapKeyDown, evenFrame;
|
|
Boolean doEnemyFlapSound, doEnemyScrapeSound;
|
|
|
|
extern handInfo theHand;
|
|
extern prefsInfo thePrefs;
|
|
extern Rect playerRects[11], mainWindowRect;
|
|
extern short numUpdateRects1, numUpdateRects2, numOwls;
|
|
extern Boolean quitting, openTheScores;
|
|
|
|
|
|
//============================================================== Functions
|
|
//-------------------------------------------------------------- InitNewGame
|
|
|
|
// This funciton sets up variables and readies for a new game. It is calledÉ
|
|
// only when a the user selects "New Game" - during the course of a game, itÉ
|
|
// is not called again.
|
|
|
|
void InitNewGame (void)
|
|
{ // Initialize a number of game variables.
|
|
countDownTimer = 0; // Zero count down timer.
|
|
numLedges = 3; // Initial number of ledges (platforms).
|
|
beginOnLevel = 1; // Ledge (platform) the player is on (center ledge).
|
|
levelOn = 0; // Game level on (first level).
|
|
livesLeft = kInitNumLives; // Number of player lives remaining.
|
|
theScore = 0L; // Player's score (a long - can go to 2 billion).
|
|
playing = TRUE; // Flag playing.
|
|
pausing = FALSE; // Not paused.
|
|
evenFrame = TRUE; // Set an initial state for evenFrame.
|
|
wasTensOfThousands = 0L; // Used for noting when player gets an extra life.
|
|
numOwls = 4; // Number of "owl" enemies for this level.
|
|
|
|
numUpdateRects1 = 0; // Init number of "update" rectangles.
|
|
numUpdateRects2 = 0; // (see Render.c to see what these do)
|
|
|
|
InitHandLocation(); // Get the mummy hand down in the lava.
|
|
theHand.mode = kLurking; // Flag the hand in "lurking" mode.
|
|
|
|
SetUpLevel(); // Set up platforms for first level (wave).
|
|
|
|
DumpBackToWorkMap(); // Copy background offscreen to "work" offscreen.
|
|
|
|
UpdateLivesNumbers(); // Display number of lives remaining on screen.
|
|
UpdateScoreNumbers(); // Display the player's score (zero at this point).
|
|
UpdateLevelNumbers(); // Display the level (wave) the player is on.
|
|
|
|
GenerateEnemies(); // Prepare all enemies for this level.
|
|
ResetPlayer(TRUE); // Initialize all player variables and put on ledge.
|
|
}
|
|
|
|
//-------------------------------------------------------------- SetUpLevel
|
|
|
|
// Primarily, this function is called to set up the ledges for theÉ
|
|
// current level (wave) the player is on. It determines how manyÉ
|
|
// are required and then draws these offscreen. It also flashesÉ
|
|
// the obelisks and strikes the lightning.
|
|
|
|
void SetUpLevel (void)
|
|
{
|
|
short wasLedges, waveMultiple;
|
|
|
|
KillOffEye(); // Return eye to the aether.
|
|
|
|
wasLedges = numLedges; // Remember number of ledges.
|
|
waveMultiple = levelOn % 5; // Waves repeat every 5th wave (but harder!).
|
|
|
|
switch (waveMultiple) // See which of the 5 we're on.
|
|
{
|
|
case 0: // Waves 0, 5, 10, É
|
|
numLedges = 5; // have 5 ledges (platforms) on screen.
|
|
break;
|
|
|
|
case 1: // Waves 1, 6, 11, É
|
|
numLedges = 6; // are up to 6 ledges (platforms) on screen.
|
|
break;
|
|
|
|
case 2: // Waves 2, 7, 12, É
|
|
numLedges = 5; // return to 5 ledges (platforms) on screen.
|
|
break;
|
|
|
|
case 3: // Waves 3, 8, 13, É
|
|
numLedges = 3; // drop to 3 ledges (platforms) on screen.
|
|
break;
|
|
|
|
case 4: // Waves 4, 9, 14, É
|
|
numLedges = 6; // and return to 6 ledges (platforms) on screen.
|
|
break;
|
|
}
|
|
|
|
if (wasLedges != numLedges) // No need to redraw if platforms are unchanged.
|
|
DrawPlatforms(numLedges);
|
|
|
|
FlashObelisks(TRUE); // Flash the obelisks.
|
|
|
|
GenerateLightning(320, 429); // Lightning strikes platform 0.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
GenerateLightning(95, 289); // Lightning strikes platform 1.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
GenerateLightning(95, 110); // Lightning strikes platform 3.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
GenerateLightning(320, 195); // Lightning strikes platform 5.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
GenerateLightning(545, 110); // Lightning strikes platform 4.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
GenerateLightning(545, 289); // Lightning strikes platform 2.
|
|
StrikeLightning();
|
|
LogNextTick(2);
|
|
WaitForNextTick();
|
|
StrikeLightning();
|
|
|
|
FlashObelisks(FALSE); // "Unflash" obelisks (return to normal state).
|
|
// Play lightning sound!
|
|
PlayExternalSound(kLightningSound, kLightningPriority);
|
|
|
|
UpdateLevelNumbers(); // Display the current level on screen.
|
|
}
|
|
|
|
//-------------------------------------------------------------- ResetPlayer
|
|
|
|
// This function prepares the player - it places the player and his/her mountÉ
|
|
// in their proper starting location (depending on which platform they are toÉ
|
|
// begin on), and it sets all the player's variables to their initial state.
|
|
|
|
void ResetPlayer (Boolean initialPlace)
|
|
{
|
|
short location;
|
|
|
|
thePlayer.srcNum = 5; // Set which graphic (frame) the player is to use.
|
|
thePlayer.frame = 320; // This variable will be used as a coutndown timer.
|
|
|
|
if (initialPlace) // If "initialPlace" is TRUE, É
|
|
location = 0; // the player is to begin on the lowest platform.
|
|
else // Otherwise, a random location is chosen.
|
|
location = RandomInt(numLedges);
|
|
|
|
switch (location) // Move player horizontally and vertically to theirÉ
|
|
{ // proper location (based on ledge # they're on).
|
|
case 0:
|
|
thePlayer.h = 296 << 4; // Bottom center ledge.
|
|
thePlayer.v = 377 << 4; // We're scaling by 16.
|
|
break;
|
|
|
|
case 1:
|
|
thePlayer.h = 102 << 4; // Lower left ledge.
|
|
thePlayer.v = 237 << 4;
|
|
break;
|
|
|
|
case 2:
|
|
thePlayer.h = 489 << 4; // Lower right ledge.
|
|
thePlayer.v = 237 << 4;
|
|
break;
|
|
|
|
case 3:
|
|
thePlayer.h = 102 << 4; // Top left ledge.
|
|
thePlayer.v = 58 << 4;
|
|
break;
|
|
|
|
case 4:
|
|
thePlayer.h = 489 << 4; // Top right ledge.
|
|
thePlayer.v = 58 << 4;
|
|
break;
|
|
|
|
case 5:
|
|
thePlayer.h = 296 << 4; // Top central ledge.
|
|
thePlayer.v = 143 << 4;
|
|
break;
|
|
}
|
|
// Assign destination rectangle.
|
|
thePlayer.dest = playerRects[thePlayer.srcNum];
|
|
ZeroRectCorner(&thePlayer.dest);
|
|
OffsetRect(&thePlayer.dest, thePlayer.h >> 4, thePlayer.v >> 4);
|
|
thePlayer.wasDest = thePlayer.dest;
|
|
|
|
thePlayer.hVel = 0; // Player initially has no velocity.
|
|
thePlayer.vVel = 0;
|
|
thePlayer.facingRight = TRUE; // We're facing to the right.
|
|
thePlayer.flapping = FALSE; // We're not flapping our wings initially.
|
|
thePlayer.wrapping = FALSE; // We can't be wrapping around the edge of the screen.
|
|
thePlayer.clutched = FALSE; // The hand ain't got us.
|
|
thePlayer.mode = kIdle; // Our mode is "idle" - waiting to be "born".
|
|
if (lightningCount == 0) // Prepare for a lightning display to "birth" us.
|
|
{
|
|
lightH = thePlayer.dest.left + 24;
|
|
lightV = thePlayer.dest.bottom - 24;
|
|
lightningCount = kNumLightningStrikes;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- OffAMortal
|
|
|
|
// Alas, 'tis here that a player is brought who loses a life.
|
|
|
|
void OffAMortal (void)
|
|
{
|
|
livesLeft--; // Decrememnt number of player lives left.
|
|
|
|
if (livesLeft > 0) // Indeed, are there lives remaining?
|
|
{
|
|
ResetPlayer(FALSE); // Good, start a new one off.
|
|
UpdateLivesNumbers(); // Make note of the number of lives remaining.
|
|
}
|
|
else // Otherwise, we are at the dreaded "Game Over".
|
|
playing = FALSE; // Set flag to drop us out of game loop.
|
|
}
|
|
|
|
//-------------------------------------------------------------- DoCommandKey
|
|
|
|
// This function handles the case when the user has held down the commandÉ
|
|
// key. Note, this only applies to input when a game is in session - otherwiseÉ
|
|
// a standard event loop handles command keys and everything else.
|
|
|
|
void DoCommandKey (void)
|
|
{
|
|
if (BitTst(&theKeys, kEKeyMap)) // Test for "command - E"É
|
|
{
|
|
playing = FALSE; // which would indicate "End Game".
|
|
}
|
|
else if (BitTst(&theKeys, kPKeyMap)) // Otherwise, see if it's "command - P".
|
|
{
|
|
pausing = TRUE; // This means the player is pausing the game.
|
|
MenusReflectMode(); // Gray-out menus etc.
|
|
DumpMainToWorkMap(); // Save screen to offscreen.
|
|
}
|
|
else if (BitTst(&theKeys, kQKeyMap)) // Or perhaps the player hit "command - Q".
|
|
{
|
|
playing = FALSE; // Set flag to drop out of game loop.
|
|
quitting = TRUE; // Set flag to drop out of Glypha.
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- GetPlayerInput
|
|
|
|
// This function looks for keystrokes when a game is underway. We don't useÉ
|
|
// the more conventional event routines (like GetNextEvent()), because they'reÉ
|
|
// notoriously slow, allow background tasks, introduce possible INIT problems,É
|
|
// and we don't have to. Instead, we'll rely on GetKeys() (which has its ownÉ
|
|
// set of problems - but we deal with them).
|
|
|
|
void GetPlayerInput (void)
|
|
{
|
|
thePlayer.flapping = FALSE; // Assume we're not flapping.
|
|
thePlayer.walking = FALSE; // Assume too we're not walking.
|
|
|
|
GetKeys(theKeys); // Get the current keyboard keymap.
|
|
if (BitTst(&theKeys, kCommandKeyMap)) // See first if command key downÉ
|
|
DoCommandKey(); // and handle those seperately.
|
|
else // If not command key, continue.
|
|
{ // Look for one of the two "flap" keys.
|
|
if ((BitTst(&theKeys, kSpaceBarMap)) || (BitTst(&theKeys, kDownArrowKeyMap)))
|
|
{
|
|
if (thePlayer.mode == kIdle) // Handle special case when player is idle.
|
|
{
|
|
thePlayer.mode = kWalking; // Set the player's mode now to walking.
|
|
thePlayer.frame = 0; // Used to note "state" of walking.
|
|
} // Otherwise, if player is flying or walkingÉ
|
|
else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
|
|
{
|
|
if (!flapKeyDown) // If flap key was not down last frameÉ
|
|
{ // (this is to prevent "automatic fire").
|
|
// Give player lift.
|
|
thePlayer.vVel -= kFlapImpulse;
|
|
flapKeyDown = TRUE; // Note that the flap key is down.
|
|
// Play the "flap" sound.
|
|
PlayExternalSound(kFlapSound, kFlapPriority);
|
|
// Set player flag to indicate flapping.
|
|
thePlayer.flapping = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
flapKeyDown = FALSE; // If flap key not down, remember this.
|
|
|
|
// Test now for one of three "right" keys.
|
|
if ((BitTst(&theKeys, kRightArrowKeyMap) ||
|
|
BitTst(&theKeys, kSKeyMap) ||
|
|
BitTst(&theKeys, kQuoteMap)) &&
|
|
(thePlayer.hVel < kMaxHVelocity))
|
|
{
|
|
if (thePlayer.mode == kIdle) // Handle special case when player idle.
|
|
{ // They are to begin walking (no longer idle).
|
|
thePlayer.mode = kWalking;
|
|
thePlayer.frame = 0;
|
|
}
|
|
else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
|
|
{ // If flying or walking, player moves right.
|
|
if (!thePlayer.facingRight) // If facing left, player does an about face.
|
|
{
|
|
thePlayer.facingRight = TRUE;
|
|
if (thePlayer.clutched)
|
|
{
|
|
thePlayer.dest.left += 18;
|
|
thePlayer.dest.right += 18;
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
thePlayer.wasH = thePlayer.h;
|
|
thePlayer.wasDest = thePlayer.dest;
|
|
}
|
|
} // Otherwise, if facing right alreadyÉ
|
|
else
|
|
{ // If flying, add to their horizontal velocity.
|
|
if (thePlayer.mode == kFlying)
|
|
thePlayer.hVel += kGlideImpulse;
|
|
else // If walking, set flag to indicate a step.
|
|
thePlayer.walking = TRUE;
|
|
}
|
|
}
|
|
} // Test now for one of three "left" keys.
|
|
else if ((BitTst(&theKeys, kLeftArrowKeyMap) ||
|
|
BitTst(&theKeys, kAKeyMap) ||
|
|
BitTst(&theKeys, kColonMap)) &&
|
|
(thePlayer.hVel > -kMaxHVelocity))
|
|
{
|
|
if (thePlayer.mode == kIdle) // Handle special case when player idle.
|
|
{
|
|
thePlayer.mode = kWalking;
|
|
thePlayer.frame = 0;
|
|
}
|
|
else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
|
|
{ // If flying or walking, player moves left.
|
|
if (thePlayer.facingRight) // If facing right, player does an about face.
|
|
{ // Flag player facing left.
|
|
thePlayer.facingRight = FALSE;
|
|
if (thePlayer.clutched) // Handle case where player gripped by hand.
|
|
{ // An about face handled a bit differently.
|
|
thePlayer.dest.left -= 18;
|
|
thePlayer.dest.right -= 18;
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
thePlayer.wasH = thePlayer.h;
|
|
thePlayer.wasDest = thePlayer.dest;
|
|
}
|
|
}
|
|
else // Otherwise, player already facing left.
|
|
{ // So player will move left.
|
|
if (thePlayer.mode == kFlying)
|
|
thePlayer.hVel -= kGlideImpulse;
|
|
else
|
|
thePlayer.walking = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerIdle
|
|
|
|
// Following are a number of functions handling the player's different "modes".
|
|
// This first function handles the player when in "idle" mode. When idle, theÉ
|
|
// player is standing on a platform - having just been "born". This is when theÉ
|
|
// player is in a "safe" mode - meaning no enemy can kill them. The player remainsÉ
|
|
// in idle mode until they hit a key to flap or move or until a timer (thePlayer.frame)É
|
|
// counts down to zero.
|
|
|
|
void HandlePlayerIdle (void)
|
|
{
|
|
thePlayer.frame--; // Count down the timer.
|
|
if (thePlayer.frame == 0) // See if timer has reached zero yet.
|
|
thePlayer.mode = kWalking; // If so, player is no longer idle.
|
|
|
|
SetAndCheckPlayerDest(); // Keep player on platform.
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerFlying
|
|
|
|
// This function handles a player in "flying" mode. In flying mode, the playerÉ
|
|
// is alive and not standing/walking on any platform. A plyaer remains in flyingÉ
|
|
// mode until the player dies (collides unfavorably with an enemy), is caught byÉ
|
|
// the hand, or comes near the top of a platform (in which case they land andÉ
|
|
// switch to walking mode). While in flying mode, gravity pulls the player downÉ
|
|
// while friction acts to slow the player down.
|
|
|
|
void HandlePlayerFlying (void)
|
|
{
|
|
if (thePlayer.hVel > 0) // If player has a positive hori. velocityÉ
|
|
{ // subtract frictional constant from velocity.
|
|
thePlayer.hVel -= kAirResistance;
|
|
if (thePlayer.hVel < 0) // Don't let it go negative (otherwise, youÉ
|
|
thePlayer.hVel = 0; // can get a "yo-yo" effect set up).
|
|
}
|
|
else if (thePlayer.hVel < 0) // Otherwise, if horizontal velocity negativeÉ
|
|
{ // add firctional constant to hori. velocity.
|
|
thePlayer.hVel += kAirResistance;
|
|
if (thePlayer.hVel > 0)
|
|
thePlayer.hVel = 0;
|
|
}
|
|
|
|
thePlayer.vVel += kGravity; // Add gravity to player's vertical velocity.
|
|
|
|
if (thePlayer.vVel > kMaxVVelocity) // Don't allow player to fall too fast.
|
|
thePlayer.vVel = kMaxVVelocity;
|
|
else if (thePlayer.vVel < -kMaxVVelocity)
|
|
thePlayer.vVel = -kMaxVVelocity; // And don't allow player to climb too fast.
|
|
|
|
thePlayer.h += thePlayer.hVel; // Add velocities to players position.
|
|
thePlayer.v += thePlayer.vVel;
|
|
// Now we determine which graphic to use.
|
|
if (thePlayer.facingRight) // There are the set of right-facing graphics.
|
|
{
|
|
thePlayer.srcNum = 1; // Assume standard right-facing graphic.
|
|
if (thePlayer.vVel < -kDontFlapVel) // Now we jump through a series of hoopsÉ
|
|
{ // simply to determine whether we'll useÉ
|
|
if (thePlayer.flapping) // the graphic of the player with the wingsÉ
|
|
thePlayer.srcNum = 0; // up (srcNum = 0) or with the wings downÉ
|
|
else // (srcNum = 1).
|
|
thePlayer.srcNum = 1;
|
|
}
|
|
else if (thePlayer.vVel > kDontFlapVel)
|
|
{
|
|
if (thePlayer.flapping)
|
|
thePlayer.srcNum = 1;
|
|
else
|
|
thePlayer.srcNum = 0;
|
|
}
|
|
else if (thePlayer.flapping)
|
|
thePlayer.srcNum = 0;
|
|
}
|
|
else // If the player is facing leftÉ
|
|
{ // We jump through a similar set of hoopsÉ
|
|
thePlayer.srcNum = 2; // this time choosing between srcNum = 2 É
|
|
if (thePlayer.vVel < -kDontFlapVel) // and srcNum = 3.
|
|
{
|
|
if (thePlayer.flapping)
|
|
thePlayer.srcNum = 3;
|
|
else
|
|
thePlayer.srcNum = 2;
|
|
}
|
|
else if (thePlayer.vVel > kDontFlapVel)
|
|
{
|
|
if (thePlayer.flapping)
|
|
thePlayer.srcNum = 2;
|
|
else
|
|
thePlayer.srcNum = 3;
|
|
}
|
|
else if (thePlayer.flapping)
|
|
thePlayer.srcNum = 3;
|
|
}
|
|
|
|
SetAndCheckPlayerDest(); // Check for wrap-around, etc.
|
|
|
|
CheckLavaRoofCollision(); // See if player hit top or bottom of screen.
|
|
CheckPlayerEnemyCollision(); // See if player hit an enemy.
|
|
CheckPlatformCollision(); // See if player collided with platform.
|
|
CheckTouchDownCollision(); // See if player has landed on platform.
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerWalking
|
|
|
|
// This function handles a player in "walking" mode. They remain in this modeÉ
|
|
// until they walk off a platform's edge, flap to lift off the platform, orÉ
|
|
// collide unfavorably with an enemy (die). While in walking mode, we need onlyÉ
|
|
// determine which frame of animation to display (if the player is taking steps)É
|
|
// and check for the usual set of collisions.
|
|
|
|
void HandlePlayerWalking (void)
|
|
{
|
|
short desiredHVel;
|
|
|
|
if (thePlayer.walking) // This means user is actively holding downÉ
|
|
{ // the left or right key.
|
|
if (evenFrame) // Now we jump through a number of hoopsÉ
|
|
{ // in order to get a semi-realisticÉ
|
|
if (thePlayer.facingRight) // "stepping" animation going. We take stepsÉ
|
|
{ // only on "even frames".
|
|
if (thePlayer.srcNum == 4)
|
|
desiredHVel = 208;
|
|
else
|
|
desiredHVel = 128;
|
|
}
|
|
else
|
|
{
|
|
if (thePlayer.srcNum == 7)
|
|
desiredHVel = -208;
|
|
else
|
|
desiredHVel = -128;
|
|
}
|
|
|
|
if (thePlayer.hVel < desiredHVel)
|
|
{
|
|
thePlayer.hVel += 80; // Move player right.
|
|
if (thePlayer.hVel > desiredHVel)
|
|
{ // This is the case where player is walking.
|
|
thePlayer.hVel = desiredHVel;
|
|
PlayExternalSound(kWalkSound, kWalkPriority);
|
|
}
|
|
else // In this case, player is skidding.
|
|
PlayExternalSound(kScreechSound, kScreechPriority);
|
|
}
|
|
else
|
|
{
|
|
thePlayer.hVel -= 80; // Move player to the left.
|
|
if (thePlayer.hVel < desiredHVel)
|
|
{ // Player is stepping to left.
|
|
thePlayer.hVel = desiredHVel;
|
|
PlayExternalSound(kWalkSound, kWalkPriority);
|
|
}
|
|
else // Player is skidding to a stop.
|
|
PlayExternalSound(kScreechSound, kScreechPriority);
|
|
}
|
|
}
|
|
}
|
|
else // If user is not actively holding down theÉ
|
|
{ // left or right key, bring player to a stop.
|
|
thePlayer.hVel -= thePlayer.hVel / 4;
|
|
if ((thePlayer.hVel < 4) && (thePlayer.hVel > -4))
|
|
thePlayer.hVel = 0; // If close to zero (within 4), stop player.
|
|
else // Othewrwise, play the skidding sound.
|
|
PlayExternalSound(kScreechSound, kScreechPriority);
|
|
}
|
|
|
|
if (thePlayer.vVel > kMaxVVelocity) // Keep player from moving too quicklyÉ
|
|
thePlayer.vVel = kMaxVVelocity; // left or right.
|
|
else if (thePlayer.vVel < -kMaxVVelocity)
|
|
thePlayer.vVel = -kMaxVVelocity;
|
|
|
|
thePlayer.h += thePlayer.hVel; // Move player horizontally and verticallyÉ
|
|
thePlayer.v += thePlayer.vVel; // by the corresponding velocity.
|
|
|
|
if (thePlayer.walking) // "If player holding down left or right keysÉ".
|
|
{
|
|
if (evenFrame) // Here's where we toggle between the twoÉ
|
|
{ // frames of "stepping" animation.
|
|
if (thePlayer.facingRight)
|
|
thePlayer.srcNum = 9 - thePlayer.srcNum;
|
|
else
|
|
thePlayer.srcNum = 13 - thePlayer.srcNum;
|
|
}
|
|
}
|
|
else // If the player not holding down keysÉ
|
|
{ // draw the player just standing there.
|
|
if (thePlayer.facingRight)
|
|
thePlayer.srcNum = 5;
|
|
else
|
|
thePlayer.srcNum = 6;
|
|
}
|
|
|
|
SetAndCheckPlayerDest(); // Check for wrap-around and all that.
|
|
|
|
CheckTouchDownCollision(); // See if player still on platform.
|
|
KeepPlayerOnPlatform(); // Don't let player "sink through" ledge.
|
|
CheckPlayerEnemyCollision(); // See if player hit an enemy.
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerSinking
|
|
|
|
// When the player is in "sinking" mode, they are on a one-way ticket to death.
|
|
// The player is sinking into the lava. We put the player into this mode (ratherÉ
|
|
// than kill them outright) so that we can have a number of frames of them slowlyÉ
|
|
// slipping beneath the surface of the lava. When the get below the surface ofÉ
|
|
// the lava, they will be officially "killed" and a new player will be "born",
|
|
|
|
void HandlePlayerSinking (void)
|
|
{
|
|
thePlayer.hVel = 0; // Don't allow horizontal motion.
|
|
thePlayer.vVel = 16; // They will sink at this constant rate.
|
|
if (thePlayer.dest.top > kLavaHeight) // See if they slipped below the surface.
|
|
OffAMortal(); // If they did, kill 'em.
|
|
|
|
thePlayer.v += thePlayer.vVel; // Otherwise, move them down a notch.
|
|
|
|
SetAndCheckPlayerDest(); // Check for wrap-around, etc.
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerFalling
|
|
|
|
// "Falling" refers to a player who is dead already but is still careeningÉ
|
|
// down the screen as a skeleton. If (when) the player lands on a ledge theyÉ
|
|
// will turn into a pile of bones for a short duration. If instead they fallÉ
|
|
// into the lava, they'll sink. In any event, it is then that they areÉ
|
|
// officially pronounced dead and a new player is born.
|
|
|
|
void HandlePlayerFalling (void)
|
|
{
|
|
if (thePlayer.hVel > 0) // Handle horizontal air resistance.
|
|
{
|
|
thePlayer.hVel -= kAirResistance;
|
|
if (thePlayer.hVel < 0)
|
|
thePlayer.hVel = 0;
|
|
}
|
|
else if (thePlayer.hVel < 0)
|
|
{
|
|
thePlayer.hVel += kAirResistance;
|
|
if (thePlayer.hVel > 0)
|
|
thePlayer.hVel = 0;
|
|
}
|
|
|
|
thePlayer.vVel += kGravity; // Add in effect of gravity.
|
|
|
|
if (thePlayer.vVel > kMaxVVelocity) // Keep player from falling too fast.
|
|
thePlayer.vVel = kMaxVVelocity;
|
|
else if (thePlayer.vVel < -kMaxVVelocity)
|
|
thePlayer.vVel = -kMaxVVelocity;
|
|
|
|
thePlayer.h += thePlayer.hVel; // Move player's x and y (h and v)É
|
|
thePlayer.v += thePlayer.vVel; // by amount of velocity in each direction.
|
|
|
|
SetAndCheckPlayerDest(); // Check for wrap-around, etc.
|
|
|
|
CheckLavaRoofCollision(); // See if they hit roof or lava.
|
|
CheckPlatformCollision(); // See if they crashed to a ledge.
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandlePlayerBones
|
|
|
|
// This is when the player is just a static pile of bones on a platform. TheyÉ
|
|
// have been killed by an enemy and now are waiting to slip away so that a newÉ
|
|
// player can be born.
|
|
|
|
void HandlePlayerBones (void)
|
|
{
|
|
if (evenFrame) // To slow it down a bit, action only occursÉ
|
|
{ // on the even frames.
|
|
thePlayer.frame--; // Drop the counter down by one.
|
|
if (thePlayer.frame == 0) // When counter reaches zero, player officially dies.
|
|
OffAMortal();
|
|
else // Otherwise, player's bones are sinking.
|
|
thePlayer.dest.top = thePlayer.dest.bottom - thePlayer.frame;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- MovePlayer
|
|
|
|
// This function is the sort of "master movement" function. It looksÉ
|
|
// at what mode a player is in and calls the appropriate function fromÉ
|
|
// above. Arcade games (at least this one) tend to be very "modal" inÉ
|
|
// this way. It's the actions of the user and the enemies in the gameÉ
|
|
// that cause the player's mode to move from one state to another.
|
|
|
|
void MovePlayer (void)
|
|
{
|
|
switch (thePlayer.mode) // Check the "mode" the player is in.
|
|
{
|
|
case kIdle: // Invulnerable - standing there - just born.
|
|
HandlePlayerIdle();
|
|
break;
|
|
|
|
case kFlying: // Flapping, floating, airborne.
|
|
HandlePlayerFlying();
|
|
break;
|
|
|
|
case kWalking: // On terra firma. Standing or walking on ledge.
|
|
HandlePlayerWalking();
|
|
break;
|
|
|
|
case kSinking: // Trapped in the lava - going down.
|
|
HandlePlayerSinking();
|
|
break;
|
|
|
|
case kFalling: // Dead - a skeleton falling to earth.
|
|
HandlePlayerFalling();
|
|
break;
|
|
|
|
case kBones: // Dead - a static pile of bones on a ledge.
|
|
HandlePlayerBones();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- CheckTouchDownCollision
|
|
|
|
// This function determines whether or not the player is landed on a ledge.
|
|
// It does this by doing a rectangle collision between the player's boundingÉ
|
|
// rectangle and an imaginary rectangle enclosing an area above the ledges.
|
|
// I call these imaginary rectangles "touchDownRects[]". The trick was thatÉ
|
|
// you don't want the player to have to "hit" the top of a ledge in order toÉ
|
|
// land on it - there is an arbitrary distance above a ledge where, if the playerÉ
|
|
// is within this area, the legs ought to come out and the player flagged asÉ
|
|
// walking. As well, this same function is used for a walking player to seeÉ
|
|
// if they are still on the ledge (they may walk off the edge).
|
|
|
|
void CheckTouchDownCollision (void)
|
|
{
|
|
Rect testRect, whoCares;
|
|
short i, offset;
|
|
Boolean sected;
|
|
|
|
sected = FALSE; // Assume not on ledge.
|
|
for (i = 0; i < numLedges; i++) // Go through all ledges.
|
|
{
|
|
testRect = touchDownRects[i]; // Here's the imaginary rect.
|
|
if (thePlayer.mode == kWalking) // We need an offset if player walkingÉ
|
|
OffsetRect(&testRect, 0, 11); // since the player graphic is taller.
|
|
|
|
if (SectRect(&thePlayer.dest, &testRect, &whoCares))
|
|
{ // Does the player's rect intersect?
|
|
if (thePlayer.mode == kFlying) // Okay, it does, is the player airborne?
|
|
{
|
|
thePlayer.mode = kWalking; // Put player into walking mode.
|
|
if (thePlayer.facingRight) // Assign correct graphic for player.
|
|
thePlayer.srcNum = 5;
|
|
else
|
|
thePlayer.srcNum = 6;
|
|
if (thePlayer.vVel > 0) // Stop player from falling further.
|
|
thePlayer.vVel = 0;
|
|
thePlayer.dest.bottom += 11; // "Grow" player's bounding rect.
|
|
thePlayer.wasDest.bottom += 11;
|
|
// Move player so standing on top of ledge.
|
|
offset = thePlayer.dest.bottom - testRect.bottom - 1;
|
|
thePlayer.dest.bottom -= offset;
|
|
thePlayer.dest.top -= offset;
|
|
thePlayer.v = thePlayer.dest.top << 4;
|
|
// Play brief collision sound.
|
|
PlayExternalSound(kGrateSound, kGratePriority);
|
|
}
|
|
sected = TRUE; // Make note that we've landed.
|
|
}
|
|
}
|
|
|
|
if (!sected) // Now, if we didn't collideÉ
|
|
{ // were we walking?
|
|
if (thePlayer.mode == kWalking) // Did we walk off the ledge?
|
|
{
|
|
thePlayer.mode = kFlying; // Set player to flying mode.
|
|
thePlayer.dest.bottom -= 11; // Resize player's bounding rect.
|
|
thePlayer.wasDest.bottom -= 11;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- CheckPlatformCollision
|
|
|
|
// Unlike the above function, this one tests the player's bounding rect againstÉ
|
|
// the bounding rect of each ledge (not an imaginary rect above the ledge). ThisÉ
|
|
// function is primarily for (then) collisions off the bottom and sides of theÉ
|
|
// ledges. In this way, the ledges are "solid" - not able to be passed through.
|
|
|
|
void CheckPlatformCollision (void)
|
|
{
|
|
Rect hRect, vRect, whoCares;
|
|
short i, offset;
|
|
|
|
for (i = 0; i < numLedges; i++) // Walk through all ledges.
|
|
{ // Test rectangle overlap.
|
|
if (SectRect(&thePlayer.dest, &platformRects[i], &whoCares))
|
|
{ // If player intersecting ledgeÉ
|
|
hRect.left = thePlayer.dest.left; // Create our special test rect.
|
|
hRect.right = thePlayer.dest.right;
|
|
hRect.top = thePlayer.wasDest.top;
|
|
hRect.bottom = thePlayer.wasDest.bottom;
|
|
// Determine if the player hit theÉ
|
|
// top/bottom of the ledge or theÉ
|
|
// sides of the ledge.
|
|
if (SectRect(&hRect, &platformRects[i], &whoCares))
|
|
{ // We're fairly sure the player hitÉ
|
|
// the left or right edge of ledge.
|
|
if (thePlayer.h > thePlayer.wasH) // If player was heading rightÉ
|
|
{ // player will bounce to left.
|
|
offset = thePlayer.dest.right - platformRects[i].left;
|
|
thePlayer.dest.left -= offset;
|
|
thePlayer.dest.right -= offset;
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
if (thePlayer.hVel > 0) // We bounce back with 1/2 our vel.
|
|
thePlayer.hVel = -(thePlayer.hVel >> 1);
|
|
else
|
|
thePlayer.hVel = thePlayer.hVel >> 1;
|
|
} // Else if player was heading leftÉ
|
|
else if (thePlayer.h < thePlayer.wasH)
|
|
{ // player will bounce right.
|
|
offset = platformRects[i].right - thePlayer.dest.left;
|
|
thePlayer.dest.left += offset;
|
|
thePlayer.dest.right += offset;
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
if (thePlayer.hVel < 0) // We bounce back with 1/2 our vel.
|
|
thePlayer.hVel = -(thePlayer.hVel >> 1);
|
|
else
|
|
thePlayer.hVel = thePlayer.hVel >> 1;
|
|
} // Play impact sound.
|
|
PlayExternalSound(kGrateSound, kGratePriority);
|
|
}
|
|
else // It doesn't look like we hit theÉ
|
|
{ // the left or right edge of ledge.
|
|
vRect.left = thePlayer.wasDest.left;
|
|
vRect.right = thePlayer.wasDest.right;
|
|
vRect.top = thePlayer.dest.top;
|
|
vRect.bottom = thePlayer.dest.bottom;
|
|
// So we'll test top/bottom collision.
|
|
if (SectRect(&vRect, &platformRects[i], &whoCares))
|
|
{ // We've decided we've hit top/bottom.
|
|
if (thePlayer.wasV < thePlayer.v)
|
|
{ // If we were heading down (hit top)É
|
|
// keep player on top of ledge.
|
|
offset = thePlayer.dest.bottom - platformRects[i].top;
|
|
thePlayer.dest.top -= offset;
|
|
thePlayer.dest.bottom -= offset;
|
|
thePlayer.v = thePlayer.dest.top << 4;
|
|
// Play collision sound.
|
|
if (thePlayer.vVel > kDontFlapVel)
|
|
PlayExternalSound(kGrateSound, kGratePriority);
|
|
// If we were falling bones (dead)É
|
|
if (thePlayer.mode == kFalling)
|
|
{ // we'll bounce.
|
|
if ((thePlayer.dest.right - 16) > platformRects[i].right) {
|
|
thePlayer.hVel = 16;
|
|
if (thePlayer.vVel > 0)
|
|
thePlayer.vVel = -(thePlayer.vVel >> 1);
|
|
else
|
|
thePlayer.vVel = thePlayer.vVel >> 1;
|
|
}
|
|
else if ((thePlayer.dest.left + 16) < platformRects[i].left)
|
|
{
|
|
thePlayer.hVel = -16;
|
|
if (thePlayer.vVel > 0)
|
|
thePlayer.vVel = -(thePlayer.vVel >> 1);
|
|
else
|
|
thePlayer.vVel = thePlayer.vVel >> 1;
|
|
}
|
|
else // If we were nearly stoppedÉ
|
|
{ // turn into pile of bones.
|
|
PlayExternalSound(kBoom1Sound, kBoom1Priority);
|
|
thePlayer.vVel = 0;
|
|
thePlayer.mode = kBones;
|
|
thePlayer.frame = 22;
|
|
thePlayer.dest.top = thePlayer.dest.bottom - 22;
|
|
thePlayer.v = thePlayer.dest.top << 4;
|
|
thePlayer.srcNum = 10;
|
|
}
|
|
}
|
|
else // Okay, if we weren't falling bonesÉ
|
|
{ // bounce the player (-1/2 vel.).
|
|
if (thePlayer.vVel > 0)
|
|
thePlayer.vVel = -(thePlayer.vVel >> 1);
|
|
else
|
|
thePlayer.vVel = thePlayer.vVel >> 1;
|
|
}
|
|
} // If the player was instead moving upÉ
|
|
else if (thePlayer.wasV > thePlayer.v)
|
|
{ // the player likely hit the bottom ofÉ
|
|
// the ledge. Keep player below ledge.
|
|
offset = platformRects[i].bottom - thePlayer.dest.top;
|
|
thePlayer.dest.top += offset;
|
|
thePlayer.dest.bottom += offset;
|
|
thePlayer.v = thePlayer.dest.top << 4;
|
|
// Play collision sound.
|
|
PlayExternalSound(kGrateSound, kGratePriority);
|
|
if (thePlayer.vVel < 0) // Bounce player down (-1/2 vel.).
|
|
thePlayer.vVel = -(thePlayer.vVel >> 1);
|
|
else
|
|
thePlayer.vVel = thePlayer.vVel >> 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- KeepPlayerOnPlatform
|
|
|
|
// This is an alignment function. It is called only if the player is standing orÉ
|
|
// walking on a ledge. It is designed to keep the player's mount's (bird's)É
|
|
// feet firmly planted on the ledge. Consider that, with the addition of gravityÉ
|
|
// to a player's downward velocity, there is a problem where the player can appearÉ
|
|
// to slowly sink down through the ledge. There may be any number of methods youÉ
|
|
// might want to try to prevent this from becoming a problem in the first place, É
|
|
// but my experience has been that all the methods I've tried have flaws - correctingÉ
|
|
// for those flaws points out other flaws and you start getting a messy sort ofÉ
|
|
// patchwork. Should you ever get it to work, the mess that is your function has comeÉ
|
|
// to resemble the Knot of ????.
|
|
|
|
void KeepPlayerOnPlatform (void)
|
|
{
|
|
Rect whoCares;
|
|
short i, offset;
|
|
|
|
for (i = 0; i < numLedges; i++) // For each ledge for this waveÉ
|
|
{ // test for a collision.
|
|
if ((SectRect(&thePlayer.dest, &platformRects[i], &whoCares)) && (thePlayer.vVel > 0))
|
|
{ // If collided (player sinking), forceÉ
|
|
// player to top of ledge.
|
|
offset = thePlayer.dest.bottom - platformRects[i].top - 1;
|
|
thePlayer.dest.top -= offset;
|
|
thePlayer.dest.bottom -= offset;
|
|
thePlayer.v = thePlayer.dest.top * 16;
|
|
}
|
|
}
|
|
|
|
if (thePlayer.vVel > 0) // Set player's vertical velocity to zero.
|
|
thePlayer.vVel = 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------- CheckLavaRoofCollision
|
|
|
|
// This is a simple high/low test to see if the player has either bounced offÉ
|
|
// the roof of the "arena" or dipped down into the lava below.
|
|
|
|
void CheckLavaRoofCollision (void)
|
|
{
|
|
short offset;
|
|
|
|
if (thePlayer.dest.bottom > kLavaHeight) // See if player in lava.
|
|
{
|
|
if (thePlayer.mode == kFalling) // If falling (dead), "Splash!"
|
|
PlayExternalSound(kSplashSound, kSplashPriority);
|
|
else // If flying (alive), "Yeow!"
|
|
PlayExternalSound(kBirdSound, kBirdPriority);
|
|
thePlayer.mode = kSinking; // Irregardless, player is now sinking.
|
|
}
|
|
else if (thePlayer.dest.top < kRoofHeight) // See if player hit roof.
|
|
{ // Move player to below roof.
|
|
offset = kRoofHeight - thePlayer.dest.top;
|
|
thePlayer.dest.top += offset;
|
|
thePlayer.dest.bottom += offset;
|
|
thePlayer.v = thePlayer.dest.top * 16;
|
|
// Play collision sound.
|
|
PlayExternalSound(kGrateSound, kGratePriority);
|
|
thePlayer.vVel = thePlayer.vVel / -2; // Rebound player (-1/2 vel.).
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- SetAndCheckPlayerDest
|
|
|
|
// This function keeps our player's screen coordinates and "scaled" coordinatesÉ
|
|
// in agreement. As well, it checks for wrap-around and handles it.
|
|
|
|
void SetAndCheckPlayerDest (void)
|
|
{
|
|
short wasTall, wasWide;
|
|
// Remember width and height of player.
|
|
wasTall = thePlayer.dest.bottom - thePlayer.dest.top;
|
|
wasWide = thePlayer.dest.right - thePlayer.dest.left;
|
|
// Convert scaled coords to screen coords.
|
|
thePlayer.dest.left = thePlayer.h >> 4;
|
|
thePlayer.dest.right = thePlayer.dest.left + wasWide;
|
|
thePlayer.dest.top = thePlayer.v >> 4;
|
|
thePlayer.dest.bottom = thePlayer.dest.top + wasTall;
|
|
|
|
if (thePlayer.dest.left > 640) // Has player left right side of arena?
|
|
{ // Wrap player back to left side of screen.
|
|
OffsetRect(&thePlayer.dest, -640, 0);
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
OffsetRect(&thePlayer.wasDest, -640, 0);
|
|
}
|
|
else if (thePlayer.dest.right < 0) // Else, has player left left side of screen?
|
|
{ // Wrap player around to right side of screen.
|
|
OffsetRect(&thePlayer.dest, 640, 0);
|
|
thePlayer.h = thePlayer.dest.left << 4;
|
|
OffsetRect(&thePlayer.wasDest, 640, 0);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleLightning
|
|
|
|
// Lightning is handled here. Obelisks are flashed, lightning is generated, É
|
|
// lighting strikes, and the lightning counter decremented. This is prettyÉ
|
|
// nice - we can just set "lightningCount" to a non-zero number and thisÉ
|
|
// function will strike lightning every fram until the counter returns to zero.
|
|
|
|
void HandleLightning (void)
|
|
{
|
|
if (lightningCount > 0) // Is lightning to strik this frame?
|
|
{ // Special frame when obelisks are lit.
|
|
if (lightningCount == kNumLightningStrikes)
|
|
FlashObelisks(TRUE);
|
|
GenerateLightning(lightH, lightV); // Create new lightning "segments".
|
|
StrikeLightning(); // Draw lightning on screen.
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- FinishLightning
|
|
|
|
// This undoes what the lightning did. It "undraws" the lightning and returnsÉ
|
|
// the obelisks to their "non lit" state. I see that it is HERE where the counterÉ
|
|
// is decremented and not in the function above.
|
|
|
|
void FinishLightning (void)
|
|
{
|
|
if (lightningCount > 0)
|
|
{
|
|
StrikeLightning(); // Undraw lightning (exclusive Or).
|
|
lightningCount--; // Descrement lightning counter.
|
|
if (lightningCount == 0) // If this is the last lightning strikeÉ
|
|
FlashObelisks(FALSE); // return obelisk to normal.
|
|
// "BOOOOM!"
|
|
PlayExternalSound(kLightningSound, kLightningPriority);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- HandleCountDownTimer
|
|
|
|
// This is a pretty boring function. It is here so that when one level ends,É
|
|
// the next one does begin immediately. It gives the player a few seconds ofÉ
|
|
// breathing time. Essentially, to engage it, we need merely set "countDownTimer"É
|
|
// to a positive number. Each frame the counter gets decremented. When itÉ
|
|
// reaches zero, the level is advanced to the next wave.
|
|
|
|
void HandleCountDownTimer (void)
|
|
{
|
|
if (countDownTimer == 0) // If already zero, do nothing.
|
|
return;
|
|
else // Otherwise, if greater than zeroÉ
|
|
{
|
|
countDownTimer--; // decrememnt counter.
|
|
if (countDownTimer == 0) // Did it just hit zero?
|
|
{
|
|
countDownTimer = 0; // Well, just to be sure (dumb line of code).
|
|
levelOn++; // Increment the level (wave) we're on.
|
|
UpdateLevelNumbers(); // Display new level on screen.
|
|
SetUpLevel(); // Set up the platforms.
|
|
GenerateEnemies(); // Ready nemesis.
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------- PlayGame
|
|
|
|
// Here is the "core" of the "game loop". When a player has elected toÉ
|
|
// begin a game, Glypha falls into this function and remains in a loopÉ
|
|
// herein until the player either quits, or loses their last "bird".
|
|
// Each pass through the main loop below constitutes one "frame" of the game.
|
|
|
|
void PlayGame (void)
|
|
{
|
|
#define kTicksPerFrame 2L
|
|
Point offsetPt;
|
|
long waitUntil;
|
|
|
|
offsetPt.h = 0; // Set up ShieldCursor() point.
|
|
offsetPt.v = 20;
|
|
ShieldCursor(&mainWindowRect, offsetPt); // Hide the cursor.
|
|
waitUntil = TickCount() + kTicksPerFrame; // Set up speed governor variable.
|
|
|
|
do // Main game loop!!!!
|
|
{
|
|
MovePlayer(); // Move the player's bird.
|
|
MoveEnemies(); // Move all sphinx enemies.
|
|
HandleHand(); // Handle the mummy hand (may do nothing).
|
|
HandleEye(); // Handle eye (probably will do nothing).
|
|
DrawFrame(); // Draw the whole scene for this frame.
|
|
HandleLightning(); // Draw lightning (is applicable).
|
|
do // Here is where the speed is governed.
|
|
{
|
|
} while (TickCount() < waitUntil);
|
|
waitUntil = TickCount() + kTicksPerFrame;
|
|
evenFrame = !evenFrame; // Toggle "evenFrame" variable.
|
|
|
|
GetPlayerInput(); // Get the player's input (keystrokes).
|
|
HandleCountDownTimer(); // Handle countdown (may do nothing).
|
|
FinishLightning(); // Undraw lightning (if it needs undoing).
|
|
}
|
|
while ((playing) && (!pausing)); // Stay in loop until dead, paused or quit.
|
|
|
|
if ((!playing) && (!quitting)) // If the player died!
|
|
{ // Then play some sweet music.
|
|
PlayExternalSound(kMusicSound, kMusicPriority);
|
|
CheckHighScore(); // And see if they're on the high scores.
|
|
}
|
|
|
|
ShowCursor(); // Before we go, restore the cursor.
|
|
MenusReflectMode(); // Set the menus grayed-out state correctly.
|
|
FlushEvents(everyEvent, 0); // Flush any events in the queue.
|
|
}
|
|
|
|
//-------------------------------------------------------------- CheckHighScore
|
|
|
|
// This function handles testing to see if the player's score is in the É
|
|
// high scores. If that is the case, the function prompts the user forÉ
|
|
// a name to enter, and sorts and stores off the new score list.
|
|
|
|
void CheckHighScore (void)
|
|
{
|
|
#define kHighNameDialogID 130
|
|
Str255 placeStr, tempStr;
|
|
DialogPtr theDial;
|
|
short i, item;
|
|
Boolean leaving;
|
|
|
|
if (theScore > thePrefs.highScores[9]) // To see if on high scores, we needÉ
|
|
{ // merely see if the last guy is beat out.
|
|
openTheScores = TRUE; // Will automatically bring up high scores.
|
|
// Play some congratulatory music.
|
|
PlayExternalSound(kBonusSound, kMusicPriority - 1);
|
|
i = 8; // Find where new score fits in list.
|
|
while ((theScore > thePrefs.highScores[i]) && (i >= 0))
|
|
{ // We'll bump everyone down as we look.
|
|
thePrefs.highScores[i + 1] = thePrefs.highScores[i];
|
|
thePrefs.highLevel[i + 1] = thePrefs.highLevel[i];
|
|
PasStringCopy(thePrefs.highNames[i], thePrefs.highNames[i + 1]);
|
|
i--;
|
|
}
|
|
|
|
i++; // i is our place in list (zero based).
|
|
thePrefs.highScores[i] = theScore; // Pop the new score in place.
|
|
thePrefs.highLevel[i] = levelOn + 1; // Drop in the new highest level.
|
|
|
|
NumToString((long)i + 1L, placeStr); // Convert place to a string to displayÉ
|
|
ParamText(placeStr, "\p", "\p", "\p"); // in the dialog (via ParamText()).
|
|
|
|
InitCursor(); // Show cursor.
|
|
CenterDialog(kHighNameDialogID); // Center the dialog and then bring it up.
|
|
theDial = GetNewDialog(kHighNameDialogID, 0L, kPutInFront);
|
|
SetPort((GrafPtr)theDial);
|
|
ShowWindow((GrafPtr)theDial); // Make dialog visible.
|
|
DrawDefaultButton(theDial); // Draw outline around "Okay" button.
|
|
FlushEvents(everyEvent, 0); // Flush any events queued up.
|
|
// Put a default name in text edit box.
|
|
SetDialogString(theDial, 2, thePrefs.highName);
|
|
SelIText(theDial, 2, 0, 1024); // Select the whole text edit string.
|
|
leaving = FALSE; // Flag for noting when player hit "Okay".
|
|
|
|
while (!leaving) // Simple modal dialog loop.
|
|
{
|
|
ModalDialog(0L, &item); // Use standard filtering.
|
|
|
|
if (item == 1) // If player hit the "Okay" buttonÉ
|
|
{ // Get the name entered in text edit box.
|
|
GetDialogString(theDial, 2, tempStr);
|
|
// Copy the name into high score list.
|
|
PasStringCopyNum(tempStr, thePrefs.highNames[i], 15);
|
|
PasStringCopy(thePrefs.highNames[i], thePrefs.highName);
|
|
leaving = TRUE; // We're gone!
|
|
}
|
|
}
|
|
|
|
DisposDialog(theDial); // Clean up.
|
|
}
|
|
else // But if player didn't get on high scoresÉ
|
|
openTheScores = FALSE; // no need to rub their face in it.
|
|
}
|
|
|