/* 5/06/2008 Sean Palmer palmerfx@gmail.com C++ HW WK 10 Multiple Inheritance PacMan 2.0.5 Synopsis: Creates a rudimentary version of PacMan, counts lives and score Caveats: FIXED! : no support for 'eating' ghosts. now varies between predator and victim FIXED! : ghosts eat the dots and cherries just as PacMan does pacman is a halogen, other ranks were adjusted to play nice FIXED! : ghosts blunder around randomly. now uses vision-based barrier avoidance FIXED! : ghosts trapped by pellets. now pellets can be "masked" by other wabbits (share the same coordinate space) this introduced a bunch of exciting bugs! FIXED! : cherries are pretty stupid too right now. now uses barrier avoidance motion FIXED! : only scores when pman moves and eats something. now scores when things blunder into him FIXED! : game respawns pman at the same spot each time, if ghost is nearby (or on the same space) bad things happen. checks for this now masked wabbits redraw themselves once their occupiers leave, but there is a lag before this happens, create a flickering of the pellets class vision_avoidance uses multiple inheritance from visionary and avoidance, but I couldn't find a way to keep the inheritance virtual without compiler errors. As a result, I think there might be 2 wabbits for every vision_avoidance creature (just the predatory ghosts right now) probably still some bugs in here I haven't found at the moment. But it's running and (pretty much) working! */ #include #include #include #include #include "game.h" #include "wall.h" #include "pman.h" #include "ghost_predator.h" #include "ghost_victim.h" #include "cherry.h" #include "pellet.h" #include "power_pellet.h" game::game(unsigned short difficulty, char initial_c) : term(initial_c), difficulty_(difficulty) { if (difficulty_ < 1 || difficulty_ > 5) { cerr << "game::game(): invalid difficulty " << difficulty_ << "\n"; system("PAUSE"); exit(EXIT_FAILURE); } static const size_t xmax = 40; //number of columns in the picture //a maze of walls, 'C' is pman (you), 'o' is a big pellet, '%' are cherries //'V' are ghosts, '.' are pellets static const char a[][xmax + 1] = { //plus 1 for terminating '\0' "########################################", "#o. . . . . . . . . . . . . . . .%. . o#", "#.#####.######.####### #########.#####.#", "# . . %. . .#.# #% . . .#", "#.#####.######.####### #########.#####.#", "#%. . .V. . . . . . . . . .V. . . . . .#", "#.#####.######.######.## ##.####.### ###", "#.#####.######.# #.# #.# #.# #", "#o . . . . . .o# #.# #.# #.# #", "#####.########.# #.## ##.# #.### ###", "# #.# #.# #.V. . . . .V. . .o#", "# . . #.#####.####.#####.#", "# #.# #.# #. . .#.# #.#. . .#", "#####.########.######.#####.# #.#####.#", "#C . . . . . .%. . . . . . . . . . . .o#", "########################################", //DEBUG GRID: // "########################################", // "# #", // "# ##### ###### ################# ##### #", // "# # # # #", // "# ##### ###### ################# ##### #", // "# #", // "# ##### ###### ###### ##### #### #######", // "# ##### ###### # # # # # # # #", // "# # # # # # # # #", // "##### ######## # # ##### #### #######", // "# # # # # # #", // "# # # # # # ##### #### ##### #", // "# # # # # # # # # # #", // "##### ######## ###### ##### #### ##### #", // "#C . o . .V. . . . . . . . . . . . . .o#", // "########################################", }; nghosts_ = 0; static const size_t ymax = sizeof a / sizeof a[0]; startPos_[0] = startPos_[1] = 0; for (size_t y = 0; y < ymax; ++y) { for (size_t x = 0; x < xmax; ++x) { if (term.in_range(x, y)) { switch (a[y][x]) { //sorry the y comes before the x case ' ': break; case '#': new wall(this, x, y); break; case 'o': new power_pellet(this, x, y); break; case '.': new pellet(this, x, y); break; case 'C': new pman(this, x, y); startPos_[0] = x; startPos_[1] = y; break; case '%': new cherry(this, x, y, '#'); //avoids walls break; case 'V': //avoids walls, radius of vision in range 2-6 new ghost_predator(this, x, y, '#', difficulty_+1); ++nghosts_; break; default: cerr << "bad character '" << a[y][x] << "' at (" << x << ", " << y << ")\n"; system("PAUSE"); exit(EXIT_FAILURE); } } } } gridXmax_ = xmax; gridYmax_ = ymax; if (difficulty_ > 1) { //just being a total jerk here... for (unsigned i = 1; i < difficulty_; ++i) { unsigned newx = 0; unsigned newy = 0; findOpenSpot(&newx,&newy); new ghost_predator(this, newx, newy, '#', difficulty_+1); ++nghosts_; } } } game::~game() { //Delete any remaining wabbits. for (master_t::const_iterator it = master.begin(); it != master.end();) { wabbit *const p = *it; ++it; delete p; } //term.put(0, 0, "The game is over!"); //term.wait(5000); //Give user several seconds to read the message. } //Return the address of the "wabbit" at coordinates (x, y) in this game, //or zero if no wabbit is there. game::master_t::value_type game::get(unsigned x, unsigned y) const { for (master_t::const_iterator it = master.begin(); it != master.end(); ++it) { if ((**it).x == x && (**it).y == y ) { //found our wabbit return *it; } } return 0; //no luck } //alternate version that loops through to find EVERY wabbit at given coordinate //needed for masked objects game::master_t::value_type game::get(unsigned x, unsigned y, unsigned* i) const { unsigned start = 0; for (master_t::const_iterator it = master.begin(); it != master.end(); ++it, ++start) { if ((**it).x == x && (**it).y == y && start >= *i) { //found a wabbit, done for now //important: advance to next possible value ++(*i); return *it; } } return 0; //no luck } game::master_t::size_type game::count(char c) const { game::master_t::size_type i = 0; for (master_t::const_iterator it = master.begin(); it != master.end(); ++it) { if ((**it).c == c) { //found our type of wabbit ++i; } } return i; } void game::clearScoreLine() { for (unsigned i = 0; i < term.xmax(); ++i) { term.put(i, min(gridYmax_, term.ymax()-1)); } } void game::findOpenSpot(unsigned* x, unsigned* y) const { do { //NOTE: It's VERY important that rand() is divided by RAND_MAX first, // otherwise xmax * rand() may exceed the storage capacity for unsigned // and this routine will hang *x = static_cast(gridXmax_*(rand()/(RAND_MAX+1.0))); *y = static_cast(gridYmax_*(rand()/(RAND_MAX+1.0))); //debug code: //ostringstream os; //os << "result: " << *x << " " << *y; //term.put (0,term.ymax()-2,os.str()); } while (term.get(*x,*y) != term.background()); } void game::play() { score_ = 0; lives_ = 3 + difficulty_/2; ostringstream os; string empty = ""; long counter = 1; clearScoreLine(); time_t tstart = time(0); for (;; term.wait(250 - 50*(difficulty_-1))) { for (master_t::const_iterator it = master.begin(); it != master.end();) { os.str(empty); os << "Score: " << score_ << " Lives: " << lives_; term.put(0, min(gridYmax_, term.ymax()-1), os.str()); wabbit *const p = *it; int value = 0; char obj = '\0'; //return a value and char if wabbit moving hits something, else 0 const bool alive = p->move(&value, &obj); if (p->id() == 'C' || obj == 'C') { // only score when pman involved score_ += value; if (p->id() == 'V') { //ghost ate pman score_ += p->value(); } } ++it; if (!alive) { //The wabbit that moved in line 8 blundered into //another wabbit and was eaten. delete p; } if (count('C') == 0) { --lives_; if (lives_ >= 1) { clearScoreLine(); os.str(empty); os << "You were eaten. Lives remaining: " << lives_; term.put(0, min(gridYmax_, term.ymax()-1), os.str()); term.wait(2000); clearScoreLine(); if (!get(startPos_[0], startPos_[1])) { new pman(this, startPos_[0], startPos_[1]); } else { unsigned newx = 0; unsigned newy = 0; findOpenSpot(&newx,&newy); new pman (this, newx, newy); } } else { clearScoreLine(); term.put(0, min(gridYmax_, term.ymax()-1), "You have died of dysentary."); term.wait(2000); clearScoreLine(); os.str(empty); os << "Final Score: " << score_; term.put(0, min(gridYmax_, term.ymax()-1), os.str()); term.wait(2000); return; } } if (count('.') <=0 && count('o')<= 0) { clearScoreLine(); term.put(0, min(gridYmax_, term.ymax()-1), "WINNER IS YOU!!!"); term.wait(2000); clearScoreLine(); os.str(empty); os << "Final Score: " << score_; term.put(0, min(gridYmax_, term.ymax()-1), os.str()); term.wait(2000); return; } //if pacman ate a power pellet, destroy all ghost_predators //and put ghost_victims in their places. if (p->id() == 'C' && obj == 'o') { tstart = time(0); for (master_t::const_iterator it = master.begin(); it != master.end();) { wabbit *const o = *it; ++it; if (o->id() == 'V'){ unsigned x = o->xcord(); unsigned y = o->ycord(); delete o; new ghost_victim(this, x, y, difficulty_+1); } } } //return ghosts to predators after a few seconds if (time(0) - tstart > 10 - difficulty_ && count('u') > 0) { for (master_t::const_iterator it = master.begin(); it != master.end();) { wabbit *const o = *it; ++it; if (o->id() == 'u'){ unsigned x = o->xcord(); unsigned y = o->ycord(); delete o; new ghost_predator(this, x, y, '#', difficulty_+1); } } } //regenerate digested ghosts unsigned numGhosts = count('V') + count('u'); if ( numGhosts < nghosts_) { //for (unsigned i = 0; i < numGhosts; ++i) { unsigned newx = 0; unsigned newy = 0; findOpenSpot(&newx,&newy); new ghost_predator(this, newx, newy, '#', difficulty_+1); //} } if (score_ > counter*difficulty_*200) { ++lives_; ++counter; //term.put(0, term.ymax()-1, "Extra Life!"); } } } }