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.
G4Enemy.c
//============================================================================ |
//---------------------------------------------------------------------------- |
// Enemy.c |
//---------------------------------------------------------------------------- |
//============================================================================ |
// This file contains all enemy related functions (enemy "AI"). It handlesÉ |
// the enemy decision making proccess, moves the enemies, etc. |
#include "G4Externs.h" |
#define kEnemyImpulse 8 |
#define kOwlMaxHVel 96 |
#define kOwlMaxVVel 320 |
#define kOwlHeightSmell 96 |
#define kOwlFlapImpulse 32 |
#define kWolfMaxHVel 128 |
#define kWolfMaxVVel 400 |
#define kWolfHeightSmell 160 |
#define kWolfFlapImpulse 48 |
#define kJackalMaxHVel 192 |
#define kJackalMaxVVel 512 |
#define kJackalHeightSmell 240 |
#define kJackalFlapImpulse 72 |
Boolean SetEnemyInitialLocation (Rect *); |
void SetEnemyAttributes (short); |
short AssignNewAltitude (void); |
void InitEnemy (short, Boolean); |
void CheckEnemyPlatformHit (short); |
void CheckEnemyRoofCollision (short); |
void HandleIdleEnemies (short); |
void HandleFlyingEnemies (short); |
void HandleWalkingEnemy (short); |
void HandleSpawningEnemy (short); |
void HandleFallingEnemy (short); |
void HandleEggEnemy (short); |
void ResolveEnemyPlayerHit (short); |
handInfo theHand; |
eyeInfo theEye; |
Rect grabZone; |
short deadEnemies, spawnedEnemies, numEnemiesThisLevel, numOwls; |
extern playerType thePlayer; |
extern enemyType theEnemies[kMaxEnemies]; |
extern Rect platformRects[6], enemyInitRects[5]; |
extern long theScore; |
extern short numLedges, numEnemies, countDownTimer; |
extern short levelOn; |
extern Boolean evenFrame, doEnemyFlapSound, doEnemyScrapeSound; |
//============================================================== Functions |
//-------------------------------------------------------------- SetEnemyInitialLocation |
// When a new enemy is about to be "born", this function is called to determineÉ |
// the enemies starting location. The only thing important here is that the enemyÉ |
// appears on a valid platform for the particular level we're on. As well, whichÉ |
// platform he (it) appears on should be random. |
Boolean SetEnemyInitialLocation (Rect *theRect) |
{ |
short where, possibilities; |
Boolean facing; |
possibilities = numLedges - 1; // Determine number of valid platforms. |
where = RandomInt(possibilities); // Choose one at random. |
*theRect = enemyInitRects[where]; // Initially place enemy at default location. |
switch (where) // Determine if enemy facing left or right. |
{ // It depends upon which platform they're on. |
case 0: // These are the left-most platforms. |
case 2: |
facing = TRUE; // Enemy will face right. |
break; |
case 3: // Special case for the center platform. |
if (RandomInt(2) == 0) // Enemy randomly faces either left or right. |
facing = TRUE; |
else |
facing = FALSE; |
break; |
default: // Catch remaining (right-most) platforms. |
facing = FALSE; // Enemy will face left. |
break; |
} |
if ((levelOn % 5) == 4) // Handle special case for Egg Wave |
{ // Re-define enemy bounds. |
theRect->left += 12 + RandomInt(48) - 24; |
theRect->right = theRect->left + 24; |
theRect->top = theRect->bottom - 24; |
} |
return (facing); |
} |
//-------------------------------------------------------------- SetEnemyAttributes |
// Depending upon the type of enemy this function is passed (there are threeÉ |
// types of sphinx enemies), this function sets up that enemies variousÉ |
// attributes - such as maximum vertical velocity, etc. |
void SetEnemyAttributes (short i) |
{ |
short h; |
// Point enemy toward center of screen. |
h = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1; |
if (h < 320) // If enemy in left half of screenÉ |
theEnemies[i].facingRight = TRUE; // the enemy will face to the right. |
else // Otherwise, if in right half of screenÉ |
theEnemies[i].facingRight = FALSE; // face to the left. |
switch (theEnemies[i].kind) // Okay, depending upon what "kind" of enemyÉ |
{ // we're dealing with.... |
case kOwl: // The owl is the simplest (wimpiest) enemy. |
if (theEnemies[i].facingRight) // Choose which graphic to use. |
theEnemies[i].srcNum = 0; |
else |
theEnemies[i].srcNum = 2; |
// Set owl's velocity limitations. |
theEnemies[i].maxHVel = kOwlMaxHVel; |
theEnemies[i].maxVVel = kOwlMaxVVel; |
// This is the distance within which he willÉ |
// pursue the player (it's strictly Y distance). |
theEnemies[i].heightSmell = kOwlHeightSmell; |
// This is how powerful the owl's "flap" is. |
theEnemies[i].flapImpulse = kOwlFlapImpulse; |
break; |
case kWolf: // The wolf sphinx is of medium difficulty. |
if (theEnemies[i].facingRight) // Choose which graphic to use. |
theEnemies[i].srcNum = 4; |
else |
theEnemies[i].srcNum = 6; |
// Set wolf's velocity limitations. |
theEnemies[i].maxHVel = kWolfMaxHVel; |
theEnemies[i].maxVVel = kWolfMaxVVel; |
// This is the distance within which he willÉ |
// pursue the player (it's strictly Y distance). |
theEnemies[i].heightSmell = kWolfHeightSmell; |
// This is how powerful the wolf's "flap" is. |
theEnemies[i].flapImpulse = kWolfFlapImpulse; |
break; |
case kJackal: // The jackal is the swiftest, toughest enemy. |
if (theEnemies[i].facingRight) // Choose which graphic to use. |
theEnemies[i].srcNum = 8; |
else |
theEnemies[i].srcNum = 10; |
// Set jackal's velocity limitations. |
theEnemies[i].maxHVel = kJackalMaxHVel; |
theEnemies[i].maxVVel = kJackalMaxVVel; |
// This is the distance within which he willÉ |
// pursue the player (it's strictly Y distance). |
theEnemies[i].heightSmell = kJackalHeightSmell; |
// This is how powerful the jackal's "flap" is. |
theEnemies[i].flapImpulse = kJackalFlapImpulse; |
break; |
} |
} |
//-------------------------------------------------------------- AssignNewAltitude |
// The sphinxes "patrol" specific altitudes in the arena. After wrapping aroundÉ |
// the screen a few times, they randomly select a new altitude to patrol (thisÉ |
// keeps the player from finding a "safe" place to stand. This function choosesÉ |
// a new altitude for the enemy to patrol. |
short AssignNewAltitude (void) |
{ |
short which, altitude; |
which = RandomInt(4); // There are only 4 "patrol altitudes". |
switch (which) // Depending on which random number came upÉ |
{ |
case 0: // This is just below the ceiling. |
altitude = 65 << 4; |
break; |
case 1: // This is below the top platforms but above theÉ |
altitude = 150 << 4; // center platform. |
break; |
case 2: // This is just below the center platform. |
altitude = 245 << 4; |
break; |
case 3: // This is striahgt across the lava pit. |
altitude = 384 << 4; |
break; |
} |
return (altitude); |
} |
//-------------------------------------------------------------- InitEnemy |
// This resets an enemies info. It is called when a new enemy is to be born. |
// It is called if an egg is about to hatch, if a new level has begun, or ifÉ |
// if it is simply time to add a new enemy. |
void InitEnemy (short i, Boolean reincarnated) |
{ |
Boolean facing; |
if (spawnedEnemies < numEnemiesThisLevel) // New enemy to appear (in other wordsÉ |
{ // this enemy is not hatched). |
// Call function to set new location. |
facing = SetEnemyInitialLocation(&theEnemies[i].dest); |
theEnemies[i].wasDest = theEnemies[i].dest; |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
theEnemies[i].v = theEnemies[i].dest.top << 4; |
theEnemies[i].wasH = theEnemies[i].h; // Reset "old locations" variables. |
theEnemies[i].wasV = theEnemies[i].v; |
// Assign the "patrol altitude". |
theEnemies[i].targetAlt = theEnemies[i].v - (40 << 4); |
theEnemies[i].hVel = 0; // Zero velocity vraiables. |
theEnemies[i].vVel = 0; |
theEnemies[i].pass = 0; // Zero number of times wrapped around. |
if ((levelOn % 5) == 4) // If this is an Egg WaveÉ |
theEnemies[i].mode = kEggTimer; // set enemy in "wait to hatch" mode. |
else // Otherwise, just sut enemy inÉ |
theEnemies[i].mode = kIdle; // idle mode. |
if (i < numOwls) // Determine what kind of enemy. |
theEnemies[i].kind = kOwl; |
else if (i > (numOwls + 6)) |
theEnemies[i].kind = kJackal; |
else |
theEnemies[i].kind = kWolf; |
theEnemies[i].facingRight = facing; |
SetEnemyAttributes(i); // Initialize enemy attributes. |
if (reincarnated) // If this is an egg that will hatchÉ |
theEnemies[i].frame = RandomInt(48) + 8 + (numOwls * 32); |
else |
theEnemies[i].frame = RandomInt(48) + 32 + (64 * i) + (numOwls * 32); |
if ((levelOn % 5) == 4) // If this is an Egg Wave |
theEnemies[i].kind--; // Decrement "kind" (since it's incrementedÉ |
// when they hatch). |
spawnedEnemies++; // Keep track of number of enemies active. |
} |
} |
//-------------------------------------------------------------- GenerateEnemies |
// This function is called only for a new level. It goes through andÉ |
// intializes a whole host of enemies in one go. |
void GenerateEnemies (void) |
{ |
short i; |
if ((levelOn % 5) == 4) // If this is an Egg WaveÉ |
{ |
numEnemies = kMaxEnemies; // we insist upon the maximum number of enemies. |
numEnemiesThisLevel = numEnemies; |
} |
else // If not an egg wave, use a formula to determineÉ |
{ // the max number of enemies that are to be active. |
numEnemies = ((levelOn / 5) + 2) * 2; |
if (numEnemies > kMaxEnemies) |
numEnemies = kMaxEnemies; |
numEnemiesThisLevel = numEnemies * 2; |
} |
deadEnemies = 0; // No dead enemies yet. |
// Use formula to determine the number of owlsÉ |
// to appear. This number goes down as the levelsÉ |
// increase. It is used not merely to determineÉ |
// how many owls are to appear, but also how manyÉ |
// of the more advanced enemies. For example, whenÉ |
// numOwls goes down to zero, all the enemies willÉ |
// be of the more advanced breed (wolves and jackals). |
numOwls = 4 - ((levelOn + 2) / 5); |
if (numOwls < 0) |
numOwls = 0; |
spawnedEnemies = 0; // No enemies have been "born" yet. |
// Go through and set up all the enemies. |
for (i = 0; i < numEnemies; i++) |
InitEnemy(i, FALSE); |
} |
//-------------------------------------------------------------- CheckEnemyPlatformHit |
// This is the enemy counterpart to a similarly named function that tests forÉ |
// player collsions with the platforms. |
void CheckEnemyPlatformHit (short h) |
{ |
Rect hRect, vRect, whoCares; |
short i, offset; |
for (i = 0; i < numLedges; i++) // Test all platforms. |
{ // Do a simple bounds test. |
if (SectRect(&theEnemies[h].dest, &platformRects[i], &whoCares)) |
{ // If the enemy has hit the platformÉ |
hRect.left = theEnemies[h].dest.left; // Determine if enemy hit platform sides. |
hRect.right = theEnemies[h].dest.right; |
hRect.top = theEnemies[h].wasDest.top; |
hRect.bottom = theEnemies[h].wasDest.bottom; |
// Test this new special rect to see ifÉ |
// the enemy hit on of the platform sides. |
if (SectRect(&hRect, &platformRects[i], &whoCares)) |
{ // If enemy hit from side, see which side. |
// We handle left and right seperatrelyÉ |
// so that there's no ambiguity as toÉ |
// what the new velocity and locationÉ |
// of the enemy is. If we did not do itÉ |
// this way, there is the chance that anÉ |
// enemy get's "stuck" on the edge ofÉ |
// a platform (due to round-off errors). |
if (theEnemies[h].h > theEnemies[h].wasH) |
{ // Enemy was moving right (hit left side). |
offset = theEnemies[h].dest.right - platformRects[i].left; |
// Slide enemy "off" platform. |
theEnemies[h].dest.left -= offset; |
theEnemies[h].dest.right -= offset; |
theEnemies[h].h = theEnemies[h].dest.left << 4; |
theEnemies[h].wasH = theEnemies[h].h; |
// Bounce enemy (negate velocity). |
if (theEnemies[h].hVel > 0) |
theEnemies[h].hVel = -(theEnemies[h].hVel >> 1); |
else |
theEnemies[h].hVel = theEnemies[h].hVel >> 1; |
} |
if (theEnemies[h].h < theEnemies[h].wasH) |
{ // Enemy was moving left (hit right side). |
offset = platformRects[i].right - theEnemies[h].dest.left; |
// Slide enemy "off" platform. |
theEnemies[h].dest.left += offset; |
theEnemies[h].dest.right += offset; |
theEnemies[h].h = theEnemies[h].dest.left << 4; |
theEnemies[h].wasH = theEnemies[h].h; |
// Bounce enemy (negate velocity). |
if (theEnemies[h].hVel < 0) |
theEnemies[h].hVel = -(theEnemies[h].hVel >> 1); |
else |
theEnemies[h].hVel = theEnemies[h].hVel >> 1; |
} |
doEnemyScrapeSound = TRUE; // Play a collision sound. |
// Flip enemy to face opposite direction. |
theEnemies[h].facingRight = !theEnemies[h].facingRight; |
} |
else // Enemy didn't hit from side. |
{ // See if enemy hit top/bottom. |
vRect.left = theEnemies[h].wasDest.left; |
vRect.right = theEnemies[h].wasDest.right; |
vRect.top = theEnemies[h].dest.top; |
vRect.bottom = theEnemies[h].dest.bottom; |
// Special "test rect" for top/bottom hit. |
if (SectRect(&vRect, &platformRects[i], &whoCares)) |
{ // If hit the top/bottom of platformÉ |
if (theEnemies[h].mode == kFalling) |
{ // Was the enemy a falling egg? |
// Bounce egg (with some inelasticity). |
theEnemies[i].hVel -= (theEnemies[i].hVel >> 3); |
// When the eggs velocity is betweenÉ |
// +/- 8, consider the egg at rest. |
if ((theEnemies[i].hVel < 8) && (theEnemies[i].hVel > -8)) |
{ |
if (theEnemies[i].hVel > 0) |
theEnemies[i].hVel--; |
else if (theEnemies[i].hVel < 0) |
theEnemies[i].hVel++; |
} |
} |
// Specifically, did enemy hit the top? |
if (theEnemies[h].v > theEnemies[h].wasV) |
{ // Enemy heading down (hit platform top). |
offset = theEnemies[h].dest.bottom - platformRects[i].top; |
// Move enemy up off platform. |
theEnemies[h].dest.top -= offset; |
theEnemies[h].dest.bottom -= offset; |
theEnemies[h].v = theEnemies[h].dest.top << 4; |
theEnemies[h].wasV = theEnemies[h].v; |
if (theEnemies[h].vVel > kDontFlapVel) |
doEnemyScrapeSound = TRUE; |
// "Bounce" enemy. |
if (theEnemies[h].vVel > 0) |
theEnemies[h].vVel = -(theEnemies[h].vVel >> 1); |
else |
theEnemies[h].vVel = theEnemies[h].vVel >> 1; |
if ((theEnemies[h].vVel < 8) && (theEnemies[h].vVel > -8) && |
(theEnemies[h].hVel == 0) && (theEnemies[h].mode == kFalling)) |
{ // Here we handle an egg come to rest. |
if (((theEnemies[h].dest.right - 8) > platformRects[i].right) && |
(theEnemies[h].hVel == 0)) |
{ // Special case where egg right on edge. |
theEnemies[h].hVel = 32; |
} |
else if (((theEnemies[h].dest.left + 8) < platformRects[i].left) && |
(theEnemies[h].hVel == 0)) |
{ // Special case where egg right on edge. |
theEnemies[h].hVel = -32; |
} |
else // If egg not on the edge of platformÉ |
{ // switch to "timer" mode. |
theEnemies[h].mode = kEggTimer; |
theEnemies[h].frame = (numOwls * 96) + 128; |
theEnemies[h].vVel = 0; |
} |
} |
} |
if (theEnemies[h].v < theEnemies[h].wasV) |
{ // Enemy was rising - hit bottom of platform. |
offset = theEnemies[h].dest.top - platformRects[i].bottom; |
// Slide enemy off platform. |
theEnemies[h].dest.top -= offset; |
theEnemies[h].dest.bottom -= offset; |
theEnemies[h].v = theEnemies[h].dest.top << 4; |
theEnemies[h].wasV = theEnemies[h].v; |
// Play collision sound. |
doEnemyScrapeSound = TRUE; |
// "Bounce" enemy downward from platform. |
if (theEnemies[h].vVel < 0) |
theEnemies[h].vVel = -(theEnemies[h].vVel >> 2); |
else |
theEnemies[h].vVel = theEnemies[h].vVel >> 2; |
if ((theEnemies[h].vVel < 8) && (theEnemies[h].vVel > -8) && |
(theEnemies[h].hVel == 0) && (theEnemies[h].mode == kFalling)) |
{ |
theEnemies[h].mode = kEggTimer; |
theEnemies[h].frame = (numOwls * 96) + 128; |
theEnemies[h].vVel = 0; |
} |
} |
} |
} |
} |
} |
} |
//-------------------------------------------------------------- CheckEnemyRoofCollision |
// Like the player counterpart, this function checks to see if an enemy has hitÉ |
// the ceiling or the lava. It handles the consequences of both cases. |
void CheckEnemyRoofCollision (short i) |
{ |
short offset; |
if (theEnemies[i].dest.top < (kRoofHeight - 2)) |
{ // If enemy has hit the ceilingÉ |
offset = kRoofHeight - theEnemies[i].dest.top; |
// Move enemy down to a "legal" altitude. |
theEnemies[i].dest.top += offset; |
theEnemies[i].dest.bottom += offset; |
theEnemies[i].v = theEnemies[i].dest.top << 4; |
// Play a collision sound. |
doEnemyScrapeSound = TRUE; |
// Bounce enemy downward. |
theEnemies[i].vVel = -(theEnemies[i].vVel >> 2); |
} |
else if (theEnemies[i].dest.top > kLavaHeight) |
{ // If enemy has fallen into lavaÉ |
// kill that enemy. |
theEnemies[i].mode = kDeadAndGone; |
deadEnemies++; |
// Play a splash sound. |
{ |
short left = theEnemies[i].dest.left; |
short right = theEnemies[i].dest.right; |
short bottom = theEnemies[i].dest.bottom; |
short delta = (right - left); |
short temp = left * 32; |
short splashItr; |
for(splashItr = 0; splashItr < 32; splashItr++) |
{ |
StartPixelShatter(temp / 32, bottom, (theEnemies[i].hVel * 0.75), -(theEnemies[i].vVel * 0.75), kShatterLavaSplash); |
temp += delta; |
} |
} |
PlayExternalSound(kSplashSound, kSplashPriority); |
// Call up another from the ranks. |
InitEnemy(i, TRUE); |
} |
} |
//-------------------------------------------------------------- HandleIdleEnemies |
// The following functions handle the various enemy modes. Enemies areÉ |
// considered to be in a specific mode and each mode is handled differently. |
// Idle enemies are ones who are "invisible" - not yet born. While idle, aÉ |
// timer is ticking down - when it reaches zero, the enemy appears. |
void HandleIdleEnemies (short i) |
{ |
theEnemies[i].frame--; // Decrement timer. |
if (theEnemies[i].frame <= 0) // If timer is zero or lessÉ |
{ |
theEnemies[i].mode = kSpawning; // enemy is "born". |
theEnemies[i].wasH = theEnemies[i].h; |
theEnemies[i].wasV = theEnemies[i].v; |
theEnemies[i].hVel = 0; |
theEnemies[i].vVel = 0; |
theEnemies[i].frame = 0; |
SetEnemyAttributes(i); // Initialize enemy attributes. |
PlayExternalSound(kSpawnSound, kSpawnPriority); |
} |
} |
//-------------------------------------------------------------- HandleFlyingEnemies |
// Once an enemy takes off from a platform, they will always be in flying modeÉ |
// unless they should be killed. This function handles the flying mode. |
void HandleFlyingEnemies (short i) |
{ |
short dist; |
Boolean shouldFlap; |
// Take into account gravity pulling enemy down. |
theEnemies[i].vVel += kGravity; |
// Get absolute difference in enemy/player altitude. |
dist = thePlayer.dest.top - theEnemies[i].dest.top; |
if (dist < 0) |
dist = -dist; |
// See if the player is within the enemy's "seek" range. |
if ((dist < theEnemies[i].heightSmell) && |
((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))) |
{ // Enemy will actively seek the player. |
if (thePlayer.dest.left < theEnemies[i].dest.left) |
{ // Determine if quicker to go left or right to get player. |
dist = theEnemies[i].dest.left - thePlayer.dest.left; |
if (dist < 320) // Closest route is to the left. |
theEnemies[i].facingRight = FALSE; |
else // Closest route is to the right. |
theEnemies[i].facingRight = TRUE; |
} |
else if (thePlayer.dest.left > theEnemies[i].dest.left) |
{ // Determine if quicker to go left or right to get player. |
dist = thePlayer.dest.left - theEnemies[i].dest.left; |
if (dist < 320) // Closest route is to the right. |
theEnemies[i].facingRight = TRUE; |
else // Closest route is to the left. |
theEnemies[i].facingRight = FALSE; |
} |
// Seek an altitude 16 pixels above player. |
if (((theEnemies[i].v + 16) > thePlayer.v) && (evenFrame)) |
shouldFlap = TRUE; |
else |
shouldFlap = FALSE; |
} |
else // Else, player not within enemy's "seek" altitude. |
{ // Flap if necessary to maintain "patrol altitude". |
if ((theEnemies[i].v > theEnemies[i].targetAlt) && (evenFrame)) |
shouldFlap = TRUE; |
else |
shouldFlap = FALSE; |
} |
if (shouldFlap) // If the enemy has determined that it needs to flapÉ |
{ // Give the enemy lift & play the flap sound. |
theEnemies[i].vVel -= theEnemies[i].flapImpulse; |
doEnemyFlapSound = TRUE; |
} |
// Enemy never hovers - must move right or left. |
if (theEnemies[i].facingRight) |
{ // If enemy facing right - move enemy to the right. |
theEnemies[i].hVel += kEnemyImpulse; |
if (theEnemies[i].hVel > theEnemies[i].maxHVel) |
theEnemies[i].hVel = theEnemies[i].maxHVel; |
// Determine correct graphic for enemy. |
switch (theEnemies[i].kind) |
{ |
case kOwl: |
if (shouldFlap) |
theEnemies[i].srcNum = 12; |
else |
theEnemies[i].srcNum = 13; |
break; |
case kWolf: |
if (shouldFlap) |
theEnemies[i].srcNum = 16; |
else |
theEnemies[i].srcNum = 17; |
break; |
case kJackal: |
if (shouldFlap) |
theEnemies[i].srcNum = 20; |
else |
theEnemies[i].srcNum = 21; |
break; |
} |
} |
else // If enemy not facing right (left) move to the left. |
{ |
theEnemies[i].hVel -= kEnemyImpulse; |
if (theEnemies[i].hVel < -theEnemies[i].maxHVel) |
theEnemies[i].hVel = -theEnemies[i].maxHVel; |
// Determine correct graphic for enemy. |
switch (theEnemies[i].kind) |
{ |
case kOwl: |
if (shouldFlap) |
theEnemies[i].srcNum = 14; |
else |
theEnemies[i].srcNum = 15; |
break; |
case kWolf: |
if (shouldFlap) |
theEnemies[i].srcNum = 18; |
else |
theEnemies[i].srcNum = 19; |
break; |
case kJackal: |
if (shouldFlap) |
theEnemies[i].srcNum = 22; |
else |
theEnemies[i].srcNum = 23; |
break; |
} |
} |
// Move enemy horizontally based on hori velocity. |
theEnemies[i].h += theEnemies[i].hVel; |
theEnemies[i].dest.left = theEnemies[i].h >> 4; |
theEnemies[i].dest.right = theEnemies[i].dest.left + 64; |
// Move enemy vertically based on vertical velocity. |
theEnemies[i].v += theEnemies[i].vVel; |
theEnemies[i].dest.top = theEnemies[i].v >> 4; |
theEnemies[i].dest.bottom = theEnemies[i].dest.top + 40; |
// Check for wrap-around. |
if (theEnemies[i].dest.left > 640) |
{ // If off right edge, wrap around to left side. |
OffsetRect(&theEnemies[i].dest, -640, 0); |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
OffsetRect(&theEnemies[i].wasDest, -640, 0); |
theEnemies[i].pass++; // Increment number of "wrap-arounds" for this enemy. |
if (theEnemies[i].pass > 2) // After two screen passes (wrap arounds)É |
{ // enemy patrols a new altitude. |
theEnemies[i].targetAlt = AssignNewAltitude(); |
theEnemies[i].pass = 0; |
} |
} |
else if (theEnemies[i].dest.right < 0) |
{ // If off left edge, wrap around to right side. |
OffsetRect(&theEnemies[i].dest, 640, 0); |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
OffsetRect(&theEnemies[i].wasDest, 640, 0); |
theEnemies[i].pass++; |
if (theEnemies[i].pass > 2) |
{ |
theEnemies[i].targetAlt = AssignNewAltitude(); |
theEnemies[i].pass = 0; |
} |
} |
// Throw a touch of friction into the mix. |
theEnemies[i].vVel -= theEnemies[i].vVel >> 4; |
// Keep enemies from moving excessively fast. |
if (theEnemies[i].vVel > theEnemies[i].maxVVel) |
theEnemies[i].vVel = theEnemies[i].maxVVel; |
else if (theEnemies[i].vVel < -theEnemies[i].maxVVel) |
theEnemies[i].vVel = -theEnemies[i].maxVVel; |
CheckEnemyRoofCollision(i); // Check for lava/celing collisions. |
CheckEnemyPlatformHit(i); // Check for platform collisions. |
} |
//-------------------------------------------------------------- HandleWalkingEnemy |
// This is a brief mode for an enemy. When an enemy has hatched from an egg, itÉ |
// walks only for 8 game frames at which point it takes off and flies for the restÉ |
// of its life. |
void HandleWalkingEnemy (short i) |
{ |
if (theEnemies[i].facingRight) // If enemy facing right, walk to the right. |
{ |
theEnemies[i].dest.left += 6; // Move enemy to right. |
theEnemies[i].dest.right += 6; |
switch (theEnemies[i].kind) // Determine correct graphic for walking enemy. |
{ |
case kOwl: |
theEnemies[i].srcNum = 1 - theEnemies[i].srcNum; |
break; |
case kWolf: |
theEnemies[i].srcNum = 9 - theEnemies[i].srcNum; |
break; |
case kJackal: |
theEnemies[i].srcNum = 17 - theEnemies[i].srcNum; |
break; |
} |
theEnemies[i].hVel = 6 << 4; |
} |
else // If enemy not facing right (left), walk to the left. |
{ |
theEnemies[i].dest.left -= 6; // Move enemy to left. |
theEnemies[i].dest.right -= 6; |
switch (theEnemies[i].kind) // Determine correct graphic for walking enemy. |
{ |
case kOwl: |
theEnemies[i].srcNum = 5 - theEnemies[i].srcNum; |
break; |
case kWolf: |
theEnemies[i].srcNum = 13 - theEnemies[i].srcNum; |
break; |
case kJackal: |
theEnemies[i].srcNum = 21 - theEnemies[i].srcNum; |
break; |
} |
theEnemies[i].hVel = -6 << 4; |
} |
theEnemies[i].frame++; // Increment number of frames it has walked for. |
if (theEnemies[i].frame >= 8) // If over 8, enemy takes off an flies. |
{ |
theEnemies[i].mode = kFlying; // Switch to flying mode. |
theEnemies[i].frame = 0; // Reset "frame" variable. |
switch (theEnemies[i].kind) // Determine correct graphic for flying enemy. |
{ |
case kOwl: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 12; |
else |
theEnemies[i].srcNum = 14; |
break; |
case kWolf: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 16; |
else |
theEnemies[i].srcNum = 18; |
break; |
case kJackal: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 20; |
else |
theEnemies[i].srcNum = 22; |
break; |
} |
// Re-size enemy bounds to a "flying" size. |
theEnemies[i].dest.left -= 8; |
theEnemies[i].dest.right += 8; |
theEnemies[i].dest.bottom = theEnemies[i].dest.top + 40; |
theEnemies[i].h = theEnemies[i].dest.left * 16; |
theEnemies[i].v = theEnemies[i].dest.top * 16; |
} |
} |
//-------------------------------------------------------------- HandleSpawningEnemy |
// This is an enemy "rising out of a platform". Either an egg has just hatchedÉ |
// or a brand new enemy has been introduced. Irregardless, the sphinx is born. |
// When the enemy is at its full height, it will begin to walk. |
void HandleSpawningEnemy (short i) |
{ |
theEnemies[i].frame++; // Advance timer. |
if (theEnemies[i].frame >= 48) // If timer >= 48, enemy begins to walk. |
{ |
theEnemies[i].mode = kWalking; |
theEnemies[i].frame = 0; |
switch (theEnemies[i].kind) // Determine appropriate graphic. |
{ |
case kOwl: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 0; |
else |
theEnemies[i].srcNum = 2; |
break; |
case kWolf: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 4; |
else |
theEnemies[i].srcNum = 6; |
break; |
case kJackal: |
if (theEnemies[i].facingRight) |
theEnemies[i].srcNum = 8; |
else |
theEnemies[i].srcNum = 10; |
break; |
} |
} |
else // If not full height, use "timer" to determine height. |
theEnemies[i].dest.top = theEnemies[i].dest.bottom - theEnemies[i].frame; |
} |
//-------------------------------------------------------------- HandleFallingEnemy |
// A "falling" enemy is an air borne egg. The enemy was killed, turned into an egg, É |
// and the egg is in freefall. If the egg comes to rest, it will begin a countdownÉ |
// until it is hatched. |
void HandleFallingEnemy (short i) |
{ // Take into account gravity - accelerate egg down. |
theEnemies[i].vVel += kGravity; |
// Don't allow velocities to skyrocket. |
if (theEnemies[i].vVel > theEnemies[i].maxVVel) |
theEnemies[i].vVel = theEnemies[i].maxVVel; |
else if (theEnemies[i].vVel < -theEnemies[i].maxVVel) |
theEnemies[i].vVel = -theEnemies[i].maxVVel; |
if (evenFrame) // Apply friction on even frames (who knows). |
{ // "Friction" is 1/32nd of the velocity. |
theEnemies[i].hVel -= (theEnemies[i].hVel >> 5); |
if ((theEnemies[i].hVel < 32) && (theEnemies[i].hVel > -32)) |
{ |
if (theEnemies[i].hVel > 0) |
theEnemies[i].hVel--; |
else if (theEnemies[i].hVel < 0) |
theEnemies[i].hVel++; |
} |
} |
// Move egg horizontally. |
theEnemies[i].h += theEnemies[i].hVel; |
theEnemies[i].dest.left = theEnemies[i].h >> 4; |
theEnemies[i].dest.right = theEnemies[i].dest.left + 24; |
// Move egg vertically. |
theEnemies[i].v += theEnemies[i].vVel; |
theEnemies[i].dest.top = theEnemies[i].v >> 4; |
theEnemies[i].dest.bottom = theEnemies[i].dest.top + 24; |
// Check for wrap around. |
if (theEnemies[i].dest.left > 640) |
{ |
OffsetRect(&theEnemies[i].dest, -640, 0); |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
OffsetRect(&theEnemies[i].wasDest, -640, 0); |
} |
else if (theEnemies[i].dest.right < 0) |
{ |
OffsetRect(&theEnemies[i].dest, 640, 0); |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
OffsetRect(&theEnemies[i].wasDest, 640, 0); |
} |
CheckEnemyRoofCollision(i); // See if egg hit ceiling or lava. |
CheckEnemyPlatformHit(i); // Handle platform hit (it is here it determines ifÉ |
// egg has come to rest and should begin countdown). |
} |
//-------------------------------------------------------------- HandleEggEnemy |
// This is the "idle" egg mode. This is a static egg, sitting peacefully onÉ |
// a platform. Waiting patiently so it might hatch into a death-sphinx andÉ |
// slaughter the player. |
void HandleEggEnemy (short i) |
{ |
short center; |
theEnemies[i].frame--; // Decrement the egg timer! |
if (theEnemies[i].frame < 24) // When it falls below 24, egg starts shrinking. |
{ // Use "frame" to determine height of egg. |
theEnemies[i].dest.top = theEnemies[i].dest.bottom - theEnemies[i].frame; |
if (theEnemies[i].frame <= 0) // When the egg is completely flat (gone)É |
{ // then BOOM! a sphinx is spawned! |
theEnemies[i].frame = 0; |
PlayExternalSound(kSpawnSound, kSpawnPriority); |
center = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1; |
// Resize enemy bounds to new "walking enemy" size. |
theEnemies[i].dest.left = center - 24; |
theEnemies[i].dest.right = center + 24; |
theEnemies[i].wasDest = theEnemies[i].dest; |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
theEnemies[i].v = theEnemies[i].dest.top << 4; |
// Set up all other enemy variables. |
theEnemies[i].wasH = theEnemies[i].h; |
theEnemies[i].wasV = theEnemies[i].v; |
theEnemies[i].hVel = 0; |
theEnemies[i].vVel = 0; |
theEnemies[i].mode = kSpawning; |
theEnemies[i].kind++; |
if (theEnemies[i].kind > kJackal) |
theEnemies[i].kind = kJackal; |
SetEnemyAttributes(i); |
} |
} |
} |
//-------------------------------------------------------------- MoveEnemies |
// This is the "master" enemy function. It goes through all the enemiesÉ |
// and calls the above functions depending upon an enemy's mode. |
void MoveEnemies (void) |
{ |
short i; |
doEnemyFlapSound = FALSE; // Intially, assume no flap or scrape sounds. |
doEnemyScrapeSound = FALSE; |
// Go through each enemy. |
for (i = 0; i < numEnemies; i++) |
{ |
switch (theEnemies[i].mode) |
{ // Handle enemy according to mode it is in. |
case kIdle: // Enemy not born yet. |
HandleIdleEnemies(i); |
break; |
case kFlying: // Enemy air borne. |
HandleFlyingEnemies(i); |
break; |
case kWalking: // Enemy just born, walking off platform. |
HandleWalkingEnemy(i); |
break; |
case kSpawning: // Enemy growing from a platform. |
HandleSpawningEnemy(i); |
break; |
case kFalling: // Enemy is an egg in flight. |
HandleFallingEnemy(i); |
break; |
case kEggTimer: // Enemy is a patient, idle, silent egg. |
HandleEggEnemy(i); |
break; |
case kDeadAndGone: // Enemy no more - gone for good this level. |
break; |
} |
} |
// If any sounds were flagged, play them. |
if (doEnemyFlapSound) |
PlayExternalSound(kFlap2Sound, kFlap2Priority); |
if (doEnemyScrapeSound) |
PlayExternalSound(kScrape2Sound, kScrape2Priority); |
// See if enough enemies were killed to advance toÉ |
// next level (wave). |
if ((deadEnemies >= numEnemiesThisLevel) && (countDownTimer == 0)) |
countDownTimer = 30; |
} |
//-------------------------------------------------------------- InitHandLocation |
// This simply sets up the hand. Puts it deep in the lava (off bottom of screen). |
void InitHandLocation (void) |
{ |
SetRect(&theHand.dest, 0, 0, 56, 57); |
OffsetRect(&theHand.dest, 48, 460); |
} |
//-------------------------------------------------------------- HandleHand |
// This is the hand "AI". The hand, like the sphinx enemies, has modes. |
void HandleHand (void) |
{ |
Rect whoCares; |
short hDiff, vDiff, pull, speed; |
switch (theHand.mode) |
{ |
case kLurking: // Hand is down, waiting for player to stray near. |
if ((thePlayer.mode == kFlying) && (SectRect(&thePlayer.dest, &grabZone, &whoCares))) |
{ // If player flies near, hand begins to reach out. |
theHand.mode = kOutGrabeth; |
InitHandLocation(); |
} |
break; |
case kOutGrabeth: // Hand is either coming after or has a hold of player. |
case kClutching: |
if (SectRect(&thePlayer.dest, &grabZone, &whoCares)) |
{ // See if player in the "grab/clutch zone". |
hDiff = theHand.dest.left - thePlayer.dest.left; |
vDiff = theHand.dest.top - thePlayer.dest.top; |
// Ah! Player caught. Move player to correctÉ |
// location relative to the hand (so the playerÉ |
// appears to, in fact, be held). |
if (thePlayer.facingRight) |
hDiff -= 3; |
else |
hDiff -= 21; |
vDiff -= 29; |
// How hard/fast the hand moves depends on level. |
speed = (levelOn >> 3) + 1; |
if (hDiff < 0) |
{ |
theHand.dest.left += speed; |
theHand.dest.right += speed; |
} |
else if (hDiff > 0) |
{ |
theHand.dest.left -= speed; |
theHand.dest.right -= speed; |
} |
if (vDiff < 0) |
{ |
theHand.dest.top += speed; |
theHand.dest.bottom += speed; |
} |
else if (vDiff > 0) |
{ |
theHand.dest.top -= speed; |
theHand.dest.bottom -= speed; |
} |
// Determine absolute distance player is from hand. |
if (hDiff < 0) |
hDiff = -hDiff; |
if (vDiff < 0) |
vDiff = -vDiff; |
if ((hDiff < 8) && (vDiff < 8)) |
{ // If player in the "hot zone", player is nabbed! |
theHand.mode = kClutching; |
thePlayer.clutched = TRUE; |
// Player's movement is severely dampened. |
thePlayer.hVel = thePlayer.hVel >> 3; |
thePlayer.vVel = thePlayer.vVel >> 3; |
// Hand pulls player down (strength is greater onÉ |
// higher levels). |
pull = levelOn << 2; |
if (pull > 48) // Set an absolute limit on hand strength. |
pull = 48; |
// Pull player donw! |
thePlayer.vVel += pull; |
theHand.dest.top = thePlayer.dest.top + 29; |
theHand.dest.bottom = theHand.dest.top + 57; |
if (thePlayer.facingRight) |
theHand.dest.left = thePlayer.dest.left + 3; |
else |
theHand.dest.left = thePlayer.dest.left + 21; |
theHand.dest.right = theHand.dest.left + 58; |
} |
else // If player not in "sweet spot", hand is seeking. |
{ |
thePlayer.clutched = FALSE; |
theHand.mode = kOutGrabeth; |
} |
} |
else // Player not even close to handÉ |
{ // Hand sinks back down into lava. |
theHand.dest.top++; |
theHand.dest.bottom++; |
// When hand is off screen, hand resumes lurking. |
if (theHand.dest.top > 460) |
theHand.mode = kLurking; |
else |
theHand.mode = kOutGrabeth; |
thePlayer.clutched = FALSE; |
} |
break; |
} |
} |
//-------------------------------------------------------------- InitEye |
// This initializes all the eye's variables. |
void InitEye (void) |
{ |
SetRect(&theEye.dest, 0, 0, 48, 31); |
OffsetRect(&theEye.dest, 296, 97); |
theEye.mode = kWaiting; |
theEye.frame = (numOwls + 2) * 720; |
theEye.srcNum = 0; |
theEye.opening = 1; |
theEye.killed = FALSE; |
theEye.entering = FALSE; |
} |
//-------------------------------------------------------------- KillOffEye |
// This function handles a "slain" eye! |
void KillOffEye (void) |
{ |
if (theEye.mode == kStalking) |
{ |
theEye.killed = TRUE; |
theEye.opening = 1; |
theEye.entering = FALSE; |
if (theEye.srcNum == 0) |
theEye.srcNum = 1; |
} |
else |
InitEye(); |
} |
//-------------------------------------------------------------- HandleEye |
// But of course, the eye has modes as well. This function handles the eyeÉ |
// depending upon the mode it is in. |
void HandleEye (void) |
{ |
short diffH, diffV, speed; |
if (theEye.mode == kStalking) // Eye is alive! |
{ |
speed = (levelOn >> 4) + 1; // How fast it moves depends on level. |
if (speed > 3) |
speed = 3; |
// When eye appears or dies, it is stationary. |
if ((theEye.killed) || (theEye.entering)) |
{ |
speed = 0; |
} |
else if ((thePlayer.mode != kFlying) && (thePlayer.mode != kWalking)) |
{ |
diffH = theEye.dest.left - 296; |
diffV = theEye.dest.bottom - 128; |
} |
else |
{ |
diffH = theEye.dest.left - thePlayer.dest.left; |
diffV = theEye.dest.bottom - thePlayer.dest.bottom; |
} |
// Find direction to player (no wrap-around for eye). |
if (diffH > 0) |
{ |
if (diffH < speed) |
theEye.dest.left -= diffH; |
else |
theEye.dest.left -= speed; |
theEye.dest.right = theEye.dest.left + 48; |
} |
else if (diffH < 0) |
{ |
if (-diffH < speed) |
theEye.dest.left -= diffH; |
else |
theEye.dest.left += speed; |
theEye.dest.right = theEye.dest.left + 48; |
} |
if (diffV > 0) |
{ |
if (diffV < speed) |
theEye.dest.bottom -= diffV; |
else |
theEye.dest.bottom -= speed; |
theEye.dest.top = theEye.dest.bottom - 31; |
} |
else if (diffV < 0) |
{ |
if (-diffV < speed) |
theEye.dest.bottom -= diffV; |
else |
theEye.dest.bottom += speed; |
theEye.dest.top = theEye.dest.bottom - 31; |
} |
theEye.frame++; // Increment eye frame (timer). |
// Determine correct graphic for eye. |
if (theEye.srcNum != 0) |
{ |
if (theEye.frame > 3) // "Eye-closing frame" holds for 3 frames. |
{ |
theEye.frame = 0; |
theEye.srcNum += theEye.opening; |
if (theEye.srcNum > 3) |
{ |
theEye.srcNum = 3; |
theEye.opening = -1; |
if (theEye.killed) |
InitEye(); |
} |
else if (theEye.srcNum <= 0) |
{ |
theEye.srcNum = 0; |
theEye.opening = 1; |
theEye.frame = 0; |
theEye.entering = FALSE; |
} |
} |
} |
else if (theEye.frame > 256) |
{ |
theEye.srcNum = 1; |
theEye.opening = 1; |
theEye.frame = 0; |
} |
// Get absolute distance from eye to player. |
diffH = theEye.dest.left - thePlayer.dest.left; |
diffV = theEye.dest.bottom - thePlayer.dest.bottom; |
if (diffH < 0) |
diffH = -diffH; |
if (diffV < 0) |
diffV = -diffV; |
// See if player close enough to be killed! |
if ((diffH < 16) && (diffV < 16) && (!theEye.entering) && |
(!theEye.killed)) // Close enough to call it a kill. |
{ |
if (theEye.srcNum == 0) // If eye was open, player is killed. |
{ // Strike lightning (hit the player). |
StartPixelShatterRect(&thePlayer.dest, 0, 0, kShatterPlayerDeath); |
thePlayer.electrical = 15; |
// Player is smokin' bones! |
thePlayer.mode = kFalling; |
if (thePlayer.facingRight) |
thePlayer.srcNum = 8; |
else |
thePlayer.srcNum = 9; |
thePlayer.dest.bottom = thePlayer.dest.top + 37; |
PlayExternalSound(kBoom2Sound, kBoom2Priority); |
} |
else // If the eye was "blinking", IT was killed! |
{ // Player killed the eye! |
if (lightningCount == 0) |
{ // Strike the eye with lightning! |
lightH = theEye.dest.left + 24; |
lightV = theEye.dest.top + 16; |
// Hit 'er with 15 bolts! |
lightningCount = 15; |
} |
theScore += 2000L; // A big 2000 points for killing the eye! |
UpdateScoreNumbers(); // Refresh score display. |
PlayExternalSound(kBonusSound, kBonusPriority); |
KillOffEye(); // Slay eye! |
} // Hey, anyone remember that giant eye fromÉ |
} // Johnny Socko and his Flying Robot? |
} // As a kid, I thought that was cool! |
else if (theEye.frame > 0) // Eye has not yet appeared, but waits, lurking! |
{ |
theEye.frame--; // Decrement eye timer. |
if (theEye.frame <= 0) // When timer hits zero, eye appears! |
{ |
theEye.mode = kStalking; // The eye is after the player! |
if (lightningCount == 0) // Strike lightning at eye! |
{ |
lightH = theEye.dest.left + 24; |
lightV = theEye.dest.top + 16; |
lightningCount = 6; |
} |
theEye.srcNum = 3; |
theEye.opening = 1; |
theEye.entering = TRUE; |
} |
} |
} |
//-------------------------------------------------------------- ResolveEnemyPlayerHit |
// Okay, a bounds test determined that the player and an enemy have collided. |
// This function looks at the two and determines who wins or if it's a draw. |
void ResolveEnemyPlayerHit (short i) |
{ |
short wasVel, diff, h, v; |
if ((theEnemies[i].mode == kFalling) || (theEnemies[i].mode == kEggTimer)) |
{ // Okay, if the enemy is an eggÉ |
deadEnemies++; // simple - the enemy dies. |
theEnemies[i].mode = kDeadAndGone; |
theScore += 500L; // Add that to our score! |
{ |
Point pt; |
pt.h = theEnemies[i].dest.left; |
pt.v = theEnemies[i].dest.top; |
StartScoreFloater(500, pt); |
} |
UpdateScoreNumbers(); |
PlayExternalSound(kBonusSound, kBonusPriority); |
InitEnemy(i, TRUE); // Reset the enemy (I guess you could say they're reincarnated. |
} |
else // Now, here's a real, live sphinx enemy. |
{ // Get their difference in altitude. |
diff = (theEnemies[i].dest.top + 25) - (thePlayer.dest.top + 19); |
if (diff < -2) // Player is bested. :( |
{ // Strike player with lightning. |
thePlayer.electrical = 15; |
StartPixelShatterRect(&thePlayer.dest, theEnemies[i].hVel, theEnemies[i].vVel, kShatterPlayerDeath); |
// Player is bones. |
thePlayer.mode = kFalling; |
if (thePlayer.facingRight) |
thePlayer.srcNum = 8; |
else |
thePlayer.srcNum = 9; |
thePlayer.dest.bottom = thePlayer.dest.top + 37; |
PlayExternalSound(kBoom2Sound, kBoom2Priority); |
} |
else if (diff > 2) // Yes! Enemy is killed! |
{ // Well ... we can't kill an enemy who is spawning. |
if ((theEnemies[i].mode == kSpawning) && (theEnemies[i].frame < 16)) |
return; |
// Resize enemy bounds (use an egg bounds). |
h = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1; |
if (theEnemies[i].mode == kSpawning) |
v = theEnemies[i].dest.bottom - 2; |
else |
v = (theEnemies[i].dest.top + theEnemies[i].dest.bottom) >> 1; |
theEnemies[i].dest.left = h - 12; |
theEnemies[i].dest.right = h + 12; |
if (theEnemies[i].mode == kSpawning) |
theEnemies[i].dest.top = v - 24; |
else |
theEnemies[i].dest.top = v - 12; |
theEnemies[i].dest.bottom = theEnemies[i].dest.top + 24; |
theEnemies[i].h = theEnemies[i].dest.left << 4; |
theEnemies[i].v = theEnemies[i].dest.top << 4; |
StartPixelShatterRect(&theEnemies[i].dest, thePlayer.hVel, thePlayer.vVel, kShatterEnemyDeath); |
// Enemy is a falling egg! |
theEnemies[i].mode = kFalling; |
theEnemies[i].wasDest = theEnemies[i].dest; |
theEnemies[i].wasH = theEnemies[i].h; |
theEnemies[i].wasV = theEnemies[i].v; |
// Give player points based on enemy kind. |
{ |
unsigned long newScore; |
Point pt; |
switch (theEnemies[i].kind) |
{ |
case kOwl: |
newScore = 500L; |
break; |
case kWolf: |
newScore = 1000L; |
break; |
case kJackal: |
newScore = 1500L; |
break; |
} |
theScore += newScore; |
pt.h = theEnemies[i].dest.left; |
pt.v = theEnemies[i].dest.top; |
StartScoreFloater(newScore, pt); |
} |
UpdateScoreNumbers(); |
PlayExternalSound(kBoom2Sound, kBoom2Priority); |
} |
else // Rare case - neither the player nor the enemy get killed. |
{ // They'll bounce off one another. |
if (theEnemies[i].hVel > 0) |
theEnemies[i].facingRight = TRUE; |
else |
theEnemies[i].facingRight = FALSE; |
PlayExternalSound(kScreechSound, kScreechPriority); |
StartPixelShatter( (theEnemies[i].dest.left + thePlayer.dest.right) / 2, |
(theEnemies[i].dest.top + thePlayer.dest.bottom) / 2, 0,0, kShatterPlayerEnemyScrape); |
} |
wasVel = thePlayer.hVel; |
thePlayer.hVel = theEnemies[i].hVel; |
theEnemies[i].hVel = wasVel; |
wasVel = thePlayer.vVel; |
thePlayer.vVel = theEnemies[i].vVel; |
theEnemies[i].vVel = wasVel; |
} |
} |
//-------------------------------------------------------------- CheckPlayerEnemyCollision |
// This is a simple "bounds test" for determining player/enemy collisions. |
void CheckPlayerEnemyCollision (void) |
{ |
Rect whoCares, playTest, wrapTest; |
short i; |
playTest = thePlayer.dest; // Make a copy of player's bounds. |
InsetRect(&playTest, 8, 8); // Shrink it by 8 pixels all 'round. |
if (thePlayer.wrapping) // Need to test 2 players if "wraparounding". |
wrapTest = thePlayer.wrap; |
InsetRect(&wrapTest, 8, 8); |
// Test all enemies. |
for (i = 0; i < numEnemies; i++) |
{ // Ignore non-existant enemies. |
if ((theEnemies[i].mode != kIdle) && (theEnemies[i].mode != kDeadAndGone)) |
{ // Simple bounds test. |
if (SectRect(&playTest, &theEnemies[i].dest, &whoCares)) |
{ // Call function to determine who wins (or tie). |
ResolveEnemyPlayerHit(i); |
} // If "wrap-arounding", test other rect. |
else if (thePlayer.wrapping) |
{ |
if (SectRect(&wrapTest, &theEnemies[i].dest, &whoCares)) |
ResolveEnemyPlayerHit(i); |
} |
} |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14