// Gameballworld.cpp: implementation of the GameBallworld class and associated classes. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "gameballworld.h" #include "force.h" #include "listener.h" #include "pop.h" //for playSound #include "spritepolygon.h" #include "critterviewer.h" #include "graphicsopengl.h" #include "graphicsmfc.h" IMPLEMENT_SERIAL( cGameBallworld, cGame, 0); IMPLEMENT_SERIAL( cCritterBallProp, cCritter, 0); IMPLEMENT_SERIAL( cCritterBallworldPlayer, cCritterArmedPlayer, 0); IMPLEMENT_SERIAL( cCritterTreasure, cCritter, 0); IMPLEMENT_SERIAL( cCritterBallWall, cCritterWall, 0); #define HANDSETVIEWERPOS /* HANDSETVIEWERPOS turns on the option of hand setting the viewer pos in the InitializeViewer call. It's more standard to comment this out, but I had some lingering problems with the default approach for a long thin world, so now I'm preferring the HANDSETVIEWERPOS optoin. The other way WORKS, but I don't TRUST it anymore for this paritcualr world as I have so often seen it arbitrarily put my view BEHIND the world (negative z), though this coudl have been due to some cCritterViewer trackplayer code thats' been recently revised. So you can probably comment out HANDSETVIEWERPOS.*/ //==============================cGameBallworld======= Real cGameBallworld::TREASURERADIUS = 1.0; Real cGameBallworld::WALLTHICKNESS = 1.0; Real cGameBallworld::LEVELTIMER = 99.0; cGameBallworld::cGameBallworld() { //Fix the menu selections you'll allow. _menuflags &= ~cGame::MENU_BOUNCEWRAP; /* Default _menuflags from cGame has autoplay off, hopper off. Turn off wrap because it's bad with gravity, we want balls bouncing on bottom.*/ // _menuflags &= ~cGame::MENU_3D; /* Turn off 3D option because it's bad with the long thin background bitmap called IDB_STREET. Other than that, though, 3D is ok for this game. */ _menuflags &= ~cGame::MENU_TRACKPLAYER; /* Don't allow user to turn off the track player option. */ _menuflags &= ~cGame::MENU_RIDEPLAYER; /* Don't ride the player */ _menuflags |= cGame::MENU_HOPPER; //Turn on hopper listener option. _menuflags &= ~cGame::MENU_CHANGELISTENER; /* It looks bad to use any listener other than hopper. */ _spritetype = cGame::ST_BUBBLES; _border.set(150.0, 20.0, 0.0); //Use a long, thin world. //setBackgroundBitmap(IDB_STREET); /* Sets the cSpriteBackground for the desired background bitmap. Note that you must call this after the _border has its size fully initialized. So each cGame child with a different border should call this again in its constructor. */ setWrapflag(cCritter::BOUNCE); _seedcount = 7; setPlayer(new cCritterBallworldPlayer(this)); //_arrayHCURSOR has only the _hCursorArrow inherited from cGame. //_arrayHCURSOR.Add(((CPopApp*)::AfxGetApp())->_hCursorPlay); //No need for the shoot cursor // Initial level is 0. _cur_level = -1; _numLevels = 3; _levels = new _level[_numLevels]; // this is high importance. gameStarted = false; _last_level = 1; _levels[0].init(11, 13, 9, 100, 50, IDB_LEVEL1); _levels[0].addEnemy(-54, 0.0, 2.0, 5.0, true, 2.0, IDB_CLOUD); _levels[0].addEnemy(-57, -5, 3.0, 3.0, false, 2.0, IDB_ITR); _levels[0].addEnemy(-50, -6, 2.5, 2.5, false, 2.0, IDB_ITR); _levels[0].addEnemy(-10, 0.0, 2.0, 2.0, true, 2.0, IDB_CLOUD); _levels[0].addEnemy(-33.5, 5, 5.7, 2.0, false, 0, IDB_FOOTBALL); _levels[0].addEnemy(27, 0.0, 4.0, 4.0, true, 2.0, IDB_CLOUD); _levels[0].addEnemy(19, -1, 4, 4, false, 2.0, IDB_ITR); _levels[0].addEnemy(-25, 4, 3.0, 2.0, false, 2.0, IDB_ITR); _levels[0].addEnemy(35, -9, 0, 0, false, 0, IDB_FOOTBALL); _levels[0].addEnemy(40, -9, 0, 0, false, 0, IDB_FOOTBALL); _levels[0].addEnemy(42, 0, 5, 5, true, 2.0, IDB_CLOUD); _levels[0].addWall(-60, -10, -54, -7); _levels[0].addWall(-54, -10, -47, -8); _levels[0].addWall(-49, 0, -40, 1); _levels[0].addWall(-35, 4, -32, 5); _levels[0].addWall(-58, 2, -55, 3.5); _levels[0].addWall(-22, -4, -19, -3); _levels[0].addWall(-30, -1, -27, 0); _levels[0].addWall(-17, 0, -14, 1); _levels[0].addWall(-5, -5, 0, -4); _levels[0].addWall(1, -3, 6, -2); _levels[0].addWall(10, 1, 25, 3); _levels[0].addWall(10, -5, 25, -3); _levels[0].addWall(29, -1, 39, 0); _levels[0].addGoodie(-47, 2, 0, 1); _levels[0].addGoodie(-45, 2, 1, 0); _levels[0].addGoodie(-43, 2, 0, 1); _levels[0].addGoodie(-41, 2, 1, 0); _levels[0].addGoodie(-28.5, 1, 1, 0); _levels[0].addGoodie(-20.5, -2, 1, 0); _levels[0].addGoodie(-56.5, 6, 1, 0); _levels[0].addGoodie(-15.5, 2, 1, 0); _levels[0].addGoodie(-28.5, 1, 1, 0); _levels[1].init(15, 12, 3, 200, 40, IDB_LEVEL2); _levels[1].addEnemy(-5, 0, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(-26, 3, 2, 3, true, -3, IDB_CLOUD); _levels[1].addEnemy(-38, 3, 2, 3, true, -3, IDB_CLOUD); _levels[1].addEnemy(-38, 0, 3, 2, false, -8, IDB_ITR); _levels[1].addEnemy(-26, 0, 3, 4, false, -10, IDB_ITR); _levels[1].addEnemy(14, -4, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(16, -4, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(18, -4, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(45, -4, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(56, -6, 0, 0, false, 0, IDB_FOOTBALL); _levels[1].addEnemy(60, 3, 4, 5, true, -3, IDB_CLOUD); _levels[1].addEnemy(63, 3, 2, 3, true, -1, IDB_CLOUD); _levels[1].addEnemy(66, 3, 3, 4, true, -3, IDB_CLOUD); _levels[1].addEnemy(69, 3, 3, 2, true, -6, IDB_CLOUD); _levels[1].addEnemy(72, 3, 1, 3, true, -4, IDB_CLOUD); _levels[1].addWall(-55, 1, -53, -10); _levels[1].addWall(-58, -5, -55, -6); _levels[1].addWall(-53, -5, -50, -6); _levels[1].addWall(-45, -1, -42, 0); _levels[1].addWall(-35, 1, -33, -10); _levels[1].addWall(-33, -5, -30, -6); _levels[1].addWall(-25, -1, -22, 0); _levels[1].addWall(-15, 1, -13, -10); _levels[1].addWall(0, -4, 2, -10); _levels[1].addWall(20, -4, 5, -10); _levels[1].addWall(40, -6, 50, -7); _levels[1].addWall(55, -4, 60, -3); _levels[1].addGoodie(-65, -10, 3, 0); _levels[1].addGoodie(-63, -10, 3, 0); _levels[1].addGoodie(-67, -10, 0, 3); _levels[2].init(2, 4, 0, 300, 30, IDB_LEVEL3); _levels[2].addEnemy(-5,0, 0, 0, false, -5, IDB_ITR); _levels[2].addEnemy(-11, 0, 0, 0, false, -5, IDB_ITR); _levels[2].addEnemy(-1, 0.0, 2.0, 2.0, true, 2.0, IDB_CLOUD); _levels[2].addWall(-4, 1, 0, 0); _levels[2].addWall(4, -7, 1, -4); _levels[2].addWall(-20, -7, -18, -5); _levels[2].addWall(20, -7.5, 22, -3); //Put in some walls in the constructor. #ifndef DEBUGDAM #else //DEBUGDAM is true _seedcount = 0; #endif //DEBUGDAM } void cGameBallworld::initializeView(CPopView *pview) { cGame::initializeView(pview); pview->setUseBackgroundBitmap(TRUE); // pview->setGraphicsClass(RUNTIME_CLASS(cGraphicsOpenGL)); //Comment this in for a 3D start pview->pviewpointcritter()->setTrackplayer(TRUE); //Always look at the player. pview->pviewpointcritter()->setFoveaProportion(0.45); /* A critterviewer has a _foveaproortion that ranges from 0.0 to 1.0. We make it smaller than the default value of 0.85 so that the trackplayer image will scroll before player gets all the way to edge. Reduce still more if you want to scroll even sooner.*/ } void cGameBallworld::initializeViewpoint(cCritterViewer *pviewer) { if (pviewer->is3D()) { #ifdef HANDSETVIEWERPOS pviewer->setViewpoint(cVector3(0.0, -8.0, 40.0), cVector(border().lox(), border().midy()), FALSE); /* The setViewpoint args are (directiontoviewer, lookatpoint, trytoseewholeworld), the default third argument is TRUE, which means to back off enough to see the whole world. But in a long thin world, we don't want to do that. */ #else //HANDSETVIEWERPOS pviewer->setViewpoint(cVector3(0.0, -0.2, 1.0), cVector(border().lox(), border().midy())); /* the default third TRUE argument will move the viewpoint far from the world and try to see it all. */ pviewer->zoom(7.0); /* The bigger the world and the further away you are, the more zoom you need. */ /* I don't like this way as it makes the player trackign a little flaky to be in a high zoom. */ #endif //HANDSETVIEWERPOS } else //2D case { pviewer->setViewpoint(cVector::ZAXIS, //pplayer()->position()); cVector(border().lox(), border().midy())); pviewer->zoom(4.0); } } void cGameBallworld::seedCritters() { pbiota()->purgeCritters(RUNTIME_CLASS(cCritterBullet)); pbiota()->purgeCritters(RUNTIME_CLASS(cCritterBallProp)); //for (int i=0; i<_seedcount; i++) // new cCritterBallProp(this); gotoNextLevel(pplayer()); pplayer()->moveTo(border().locorner()); } void cGameBallworld::adjustGameParameters() { // (1) End the game if the player is dead if (!health() && !_gameover) //Player's been killed and game's not over. { //_gameover = TRUE; if ((--((cCritterBallworldPlayer*)pplayer())->livesLeft) == 0) { CString timeMsg; timeMsg.Format("You were knocked unconscious and ITRed."); AfxMessageBox(timeMsg, MB_OK | MB_ICONINFORMATION); exit(0); } else { CString timeMsg; timeMsg.Format("Never going to make it to the Libra Complex this way. Try again!\nYou have %d lives left.", ((cCritterBallworldPlayer*)pplayer())->livesLeft); pplayer()->setHealth(10); AfxMessageBox(timeMsg, MB_OK | MB_ICONINFORMATION); } pplayer()->addScore(_scorecorrection); // So user can reach _maxscore playSound("Tada"); return; } // (2) Also don't let the ballcount diminish. //(need to recheck propcount in case we just called seedCritters). int ballcount = pbiota()->count(RUNTIME_CLASS(cCritterBallProp)); int ballstoadd = _seedcount - ballcount; //for (int i=0; i < ballstoadd; i++) // new cCritterBallProp(this); // (3) Maybe check some other conditions. if ((int)(cGameBallworld::LEVELTIMER - age()) <= 0 && !_gameover) { //_gameover = TRUE; CString timeMsg; timeMsg.Format("Ooops! You ran out of time. ITR Games are going to start without you, loser."); AfxMessageBox(timeMsg, MB_OK | MB_ICONINFORMATION); exit(0); return; } // if not moving in either direction if (pplayer()->velocity().x() == 0.0 && pplayer()->velocity().y() == 0.0) { //pplayer()->setFacing(!pplayer()->facing()); if (pplayer()->facing()) pplayer()->setSprite(new cSpriteIcon(IDB_NSTANDL)); else pplayer()->setSprite(new cSpriteIcon(IDB_NSTAND)); pplayer()->setRadius(1.5); } if (pplayer()->velocity().y() < 0.0) { //if (pplayer()->facing()) // pplayer()->setSprite(new cSpriteIcon(IDB_NSTANDL)); //else // pplayer()->setSprite(new cSpriteIcon(IDB_NSTAND)); pplayer()->setRadius(1.5); } // End level if (pplayer()->position().x() > border().hix() - 2.5) gotoNextLevel(pplayer()); /* if ( (int)(age() - ( (cCritterBallworldPlayer*) pplayer())->lasthit()) < 5) { ( (cCritterBallworldPlayer*) pplayer())->setInvincible( ( (cCritterBallworldPlayer*) pplayer())->invincible() - 1 ); } else { ( (cCritterBallworldPlayer*) pplayer())->setInvincible(0); } */ } CString cGameBallworld::statusMessage() { CString cStrStatusBar; int nUpdatesPerSecond; CString cStrUpdatespersecond; CString cStrCount; CString cStrScore; CString cStrHealth; CString cStrTimer; CString cStrLevel; CString cStrLives; if (!gamepaused()) { nUpdatesPerSecond = int(((CPopApp*)::AfxGetApp())->_timer.updatesPerSecond()); if (!nUpdatesPerSecond) cStrUpdatespersecond.Format("Less than one update per second."); else cStrUpdatespersecond.Format("Updates per second: %d.", nUpdatesPerSecond); if (((CPopApp*)::AfxGetApp())->_timer.runningNearMinSpeed()) cStrUpdatespersecond += " (Slower than Real Time)"; } else cStrUpdatespersecond.Format("Animation is paused."); cStrLives.Format("Lives: %d.", ((cCritterBallworldPlayer*)pplayer())->livesLeft); cStrScore.Format("Score: %d.", score()); cStrHealth.Format("Health: %d.", health()); cStrTimer.Format("Timer: %d.", (int)(cGameBallworld::LEVELTIMER - age())); cStrLevel.Format("Level: %d.", _cur_level+1, _last_level+1); int crittercount = _pbiota->count(RUNTIME_CLASS(cCritter)); int bulletcount = _pbiota->count(RUNTIME_CLASS(cCritterBullet)); cStrCount.Format("Ball Critters: %d.", crittercount-bulletcount-2); //Subtract two for player and treasure cStrStatusBar = cStrLevel + " " + cStrLives + " " + cStrHealth + " " + cStrTimer + " " + cStrScore + " "+ cStrCount + " " + cStrUpdatespersecond; return cStrStatusBar; } void cGameBallworld::Serialize(CArchive& ar) /* This is a bit of work, as I have to match _ppuck , _phockeyrobot , _pmygoal , _probotgoal to the pointers in the cBiota array. */ { int treasureindex; cGame::Serialize(ar); if (ar.IsStoring()) // Save { ar << _index(_ptreasure); } else //Load { ar >> treasureindex; // I have to cast from cCritter* _ptreasure = (cCritterTreasure*)(_pbiota->GetAt(treasureindex)); } } void cGameBallworld::gotoNextLevel(cCritter* player) { // resetting health if (player->health() < 10) { player->setHealth(10); } // Save the last level and increment the counter. _last_level = _cur_level; ++_cur_level; setBackgroundBitmap(_levels[_cur_level]._bgbmp); // If the game has been in progress for at least one level, // give a score bonus. (This is so that score bonus is calculated // at the last level, before we restart to level 0). if (gameStarted) player->addScore(_levels[_last_level]._scoreBonus); // Reset the level counter at the last level. if (_cur_level >= _numLevels) { playSound("Tada"); CString winMsg; winMsg.Format("You win! Your total score is %d", score()); AfxMessageBox(winMsg, MB_OK | MB_ICONINFORMATION); exit(0); _cur_level = 0; } // Set the timer. cGameBallworld::LEVELTIMER = _levels[_cur_level]._timeLimit; // If the game has been in progress for at least one level, // remove the old critters. if (gameStarted) { cGameBallworld::LEVELTIMER += .2 * (_levels[_last_level]._timeLimit - age()); pplayer()->setAge(0); for (int i = 0; i < _levels[_last_level]._numWalls; ++i) { _theseWalls[i]->setCorners( cVector2(0,0), cVector2(0,0) ); // ((cCritter*) _theseWalls[i])->die(); } for (int i = 0; i < _levels[_last_level]._numEnemies; ++i) { if (_theseEnemyIDs[i]) { _theseEnemies[i]->die(); } } for (int i = 0; i < _levels[_last_level]._numGoodies; ++i) { if (_theseGoodyIDs[i]) { _theseGoodies[i]->die(); } } delete [] _theseEnemyIDs; delete [] _theseGoodyIDs; delete [] _theseEnemies; delete [] _theseWalls; delete [] _theseGoodies; } gameStarted = true; player->moveTo(this->border().locorner()); _theseEnemyIDs = new bool [_levels[_cur_level]._numEnemies]; _theseGoodyIDs = new bool [_levels[_cur_level]._numGoodies]; _theseEnemies = new cCritterBallProp*[_levels[_cur_level]._numEnemies]; _theseWalls = new cCritterBallWall*[_levels[_cur_level]._numWalls]; _theseGoodies = new cCritterTreasure*[_levels[_cur_level]._numGoodies]; // create all the lovely enemies. for (int i = 0; i < _levels[_cur_level]._numEnemies; ++i) { cCritterBallProp* enemy; if (!(_levels[_cur_level]._enemies+i)->isFlying) enemy = new cCritterBallProp(this, (_levels[_cur_level]._enemies+i)->getVector().x() - (_levels[_cur_level]._enemies+i)->lrange, (_levels[_cur_level]._enemies+i)->getVector().x() + (_levels[_cur_level]._enemies+i)->rrange, (_levels[_cur_level]._enemies+i)->isFlying, (_levels[_cur_level]._enemies+i)->initVel, (_levels[_cur_level]._enemies+i)->pic, i); else enemy = new cCritterBallProp(this, (_levels[_cur_level]._enemies+i)->getVector().y() - (_levels[_cur_level]._enemies+i)->lrange, (_levels[_cur_level]._enemies+i)->getVector().y() + (_levels[_cur_level]._enemies+i)->rrange, (_levels[_cur_level]._enemies+i)->isFlying, (_levels[_cur_level]._enemies+i)->initVel, (_levels[_cur_level]._enemies+i)->pic, i); enemy->moveTo( (_levels[_cur_level]._enemies + i)->getVector() ); _theseEnemies[i] = enemy; _theseEnemyIDs[i] = true; } // create all the lovely walls. for (int i = 0; i < _levels[_cur_level]._numWalls; ++i) { cCritterBallWall* wall; wall = new cCritterBallWall( (_levels[_cur_level]._walls+i)->getUpperVector() , (_levels[_cur_level]._walls+i)->getLowerVector(), 0, this); _theseWalls[i] = wall; } //new cCritterTreasure( this, -12, 0, 1, 0); //new cCritterTreasure( this, -10, 0, 0, 1); // create all the goodies. for (int i = 0; i < _levels[_cur_level]._numGoodies; ++i) { cCritterTreasure* goody = new cCritterTreasure( this, (_levels[_cur_level]._goodies+i)->_x, (_levels[_cur_level]._goodies+i)->_y, (_levels[_cur_level]._goodies+i)->_health, (_levels[_cur_level]._goodies+i)->_score, i ); _theseGoodies[i] = goody; _theseGoodyIDs[i] = true; } } //====================Critters for the Ballworld Game ========================== //================================================================== //==================cCritterBallworldPlayer============= cCritterBallworldPlayer::cCritterBallworldPlayer(cGame *pownergame): cCritterArmedPlayer(pownergame) { livesLeft = 2; setFacing(false); setSprite(new cSpriteIcon(IDB_NSTAND)); setHealth(10); moveTo(_movebox.locorner()); setWrapflag(cCritter::CLAMP); //Use CLAMP so you stop dead at edges. setArmed(TRUE); //Let's use bullets. setMaxspeed(40.0); //Need good speed to overcome gravity. setListener(new cListenerHopper(8.0, 0.22, 0.6)); /* The three arguments to the cListenerHopper constructor specify the walkspeed, the hopuptime, and the hopagainwait. Walkspeed is how fast the left/right (and PgUp/PgDn if in 3D where the ground is xz plane) arrow keys move the critter, Hopuptime is how many seconds you can accelerate up with the UP arrow, and Hopupwait is how many seconds you have to wait after the end of one hop before starting a new hop. */ setListenerAcceleration(160.0); //So Hopper can overcome gravity addForce(new cForceGravity(50.0)); /* Uses gravity. Default strength is 25.0. Gravity will affect player using cListenerHopper. */ // addForce(new cForceDrag(0.5)); //Better without friction. // setDensity(8.0 * density()); //To make it fall faster, but we don't need this. setRadius(1.5); //Default cCritter::PLAYERRADIUS is 0.4. setAttitudeToMotionLock(FALSE); //It looks nicer if you don't turn the player with motion. setAttitudeTangent(cVector::XAXIS); /* Without motion lock, we need setAttitudeTangent to make sure he's pointing up at the start. */ } BOOL cCritterBallworldPlayer::collide(cCritter *pcritter) { BOOL collided = cCritter::collide(pcritter); if (!collided) return FALSE; /* If you're here, you collided. We'll treat all the guys the same -- the collision with a Treasure is different, but we let the Treasure contol that collision. */ if ((pgame()->age() - lasthit()) > (Real)1.0) { damage(1); setLasthit(pgame()->age()); } pcritter->die(); return TRUE; } //==================cCritterBallProp============= cCritterBallProp::cCritterBallProp(cGame *pownergame): cCritterArmedRobot(pownergame), dead(false) { if (pgame()) setSprite(pgame()->randomSprite(pgame()->spritetype())); addForce(new cForceGravity(25.0, cVector( 0.0, -1, 0.0))); /* Make the gravity tilt towards the left to move them along. */ addForce(new cForceDrag()); //Uses default friction strength 0.5. setDensity(2.0); // Enemies won't bounce. setBounciness(0.0); setMaxspeed(20.0); randomize(cCritter::MF_VELOCITY | cSprite::MF_RADIUS); setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast Real playerx = 0.0; if (pownergame) playerx = pownergame->pplayer()->position().x(); randomizePosition(cRealBox(cVector(playerx + 3.0,_movebox.loy()), _movebox.hicorner())); /* I put them to the right of the player */ } void cCritterBallProp::die() { pplayer()->addScore(value()); ( (cGameBallworld*) pgame())->_theseEnemyIDs[id] = false; dead = true; cCritter::die(); } void cCritterBallProp::update(CPopView *pactiveview, Real dt) { cCritterArmedRobot::update(pactiveview, dt); //Always call this first if (_outcode & BOX_LOX) /* use bitwise AND to check if a flag is set. */ delete_me(); //tell the game to remove yourself if (isFlying) { if (position().y() < left_range) { setVelocity(cVector(velocity().x(), -(velocity().y()))); moveTo(cVector(position().x(), left_range)); } else if (position().y() > right_range) { setVelocity(cVector(velocity().x(), -(velocity().y()))); moveTo(cVector(position().x(), right_range)); } } else { if (position().x() < left_range) { setVelocity(cVector(-(velocity().x()), velocity().y())); moveTo(cVector(left_range, position().y())); } else if (position().x() > right_range) { setVelocity(cVector(-(velocity().x()), velocity().y())); moveTo(cVector(right_range, position().y())); } } } // functions we've added cCritterBallProp::cCritterBallProp(cGame *pownergame, Real _left_range, Real _right_range, bool _isFlying, Real init_velocity, int _pic, int id): cCritterArmedRobot(pownergame), dead(false), left_range(_left_range), right_range(_right_range), isFlying(_isFlying), pic(_pic), id(id) { if (pgame()) { setSprite(new cSpriteIcon(pic)); setRadius(1.5); this->badtype = pic; } if (!isFlying) addForce(new cForceGravity(25.0, cVector( 0.0, -1, 0.0))); setDensity(2.0); // Enemies won't bounce. setBounciness(0.0); setMaxspeed(20.0); setMinTwitchThresholdSpeed(4.0); //Means sprite doesn't switch direction unless it's moving fast // Set horizontal/vertical velocity depending on critter motion if (!isFlying) setVelocity(cVector(-init_velocity, 0.0)); else setVelocity(cVector(0.0, -init_velocity)); /* Gun stuff */ // Only flying critters and non-moving critters shoot. if (isFlying || init_velocity == 0) { setBulletClass(RUNTIME_CLASS(cCritterBulletSilver)); setWaitShoot(3); // Flying critters shoot at the player if (isFlying) setTarget(pplayer()); else setAimVector(cVector(-1.0, 0.0)); // If not flying shoot forward makeServiceRequest("move_to_front"); } else { // Moving dudes dont get to shoot. setArmed(false); } } //===================cCritterDamWall cCritterBallWall::cCritterBallWall(const cVector &enda, const cVector &endb, Real thickness, cGame *pownergame): cCritterWall(enda, endb, thickness, pownergame) { //Don't presently do anything else. Could change color with a line like //psprite()->setFillColor(cColorStyle::CN_LIGHTYELLOW); } //==================cCritterTreasure============= cCritterTreasure::cCritterTreasure(cGame *pownergame, Real xPosition, Real yPosition, int _health, int _score, int _id): cCritter(pownergame), health(_health), score(_score), id(_id) { _collidepriority = cCollider::CP_PLAYER + 1; /* Let this guy call collide on the player, as his method is overloaded in a special way. */ /* Set sprite to health or score depending on which is set. */ if (health >= score) { setSprite(new cSpriteIcon(IDB_HEALTH)); } else { setSprite(new cSpriteIcon(IDB_TREASURE)); } setRadius(0.7); setFixedflag(TRUE); moveTo(cVector(xPosition, yPosition)); } int cCritterTreasure::collidesWith(cCritter *pothercritter) { if (pothercritter->IsKindOf(RUNTIME_CLASS(cCritterBallworldPlayer))) return cCollider::COLLIDEASCALLER; else return cCollider::DONTCOLLIDE; } BOOL cCritterTreasure::collide(cCritter *pcritter) { BOOL collided = cCritter::collide(pcritter); if (!collided) return FALSE; pplayer()->addScore(score); pplayer()->addHealth(health); /* Kill the treasure */ this->die(); return TRUE; } void cCritterTreasure::die() { ( (cGameBallworld*) pgame())->_theseGoodyIDs[id] = false; cCritter::die(); }