RCRPG/C++98

Revision as of 15:36, 18 January 2020 by Thundergnat (talk | contribs) (Avoid miscounting tasks twice)

Yet another C++ RCRPG, this one has some tweaks though:

  • You can lose: If you try to break one of the cave's boundary walls, your sledge will break. If that happens in a room that has no exits (first room), you are trapped and dies!
  • If you enter a room that you visited, at least 3 minutes ago, there is a 40% chance that the loot will re-spawn.
  • The Treasure Room is placed at random in the cave.

C++


<lang cpp>

  1. include <stdio.h>
  2. include <time.h>
  3. include <iostream>
  4. include <algorithm>
  5. include <map>
  6. include <vector>
  7. include <sstream>

typedef unsigned uint;

const std::string directions[] = { "north", "south", "east", "west", "up", "down" }; const std::string objectName[] = { "nothing", "gold", "ladder", "sledge" }; const uint MAX_DOORS = sizeof( directions ) / sizeof( directions[0] ), MX_R = 26;

enum keyWord { north, south, east, west, up, down, inventory, unequip, look, help, quit, attack, drop, take, equip, alias, name, all, nothing, gold, ladder, sledge }; const keyWord inverted[] = { south, north, west, east, down, up };

class position { public:

   position() : x(0), y(0), z(0) { }
   position( int a, int b, int c ) : x(a), y(b), z(c) { }
   void set( int a, int b, int c ) {
        x = a; y = b; z = c;
   }
   bool operator ==( const position o ) { 
       return o.x == x && o.y == y && o.z == z; 
   }
   position& operator =( position o ) {
       x = o.x; y = o.y; z = o.z; 
       return *this;
   }
   position& operator +=( const position& o ) {
       x += o.x; y += o.y; z += o.z;
       return *this; 
   }
   friend position operator+( position l, const position& r ) {
       return l += r;
   }
   int    x, y, z;

};

const position dirVec[] = { position( 0, -1, 0 ), position( 0, 1, 0 ), position( 1, 0, 0 ), position( -1, 0, 0 ), position( 0, 0, -1 ), position( 0, 0, 1 ) };

typedef struct {

   keyWord action, subKey;
   std::string str1, str2;

}command;

typedef void ( *callee )( command );

class parser { public:

   parser() {
       std::string c[] = { directions[0], directions[1], directions[2], directions[3], directions[4], directions[5], "inventory", "unequip", 
           "look", "help", "quit", "attack", "drop", "take", "equip", "alias", "name", "all", objectName[0], objectName[1], objectName[2], objectName[3] };
       for( uint i = 0; i < sizeof( c ) / sizeof( c[0] ); i++ ) {
           actions.insert( std::make_pair( hashStr( c[i] ), static_cast<keyWord>( i ) ) );
           names.insert( std::make_pair( static_cast<keyWord>( i ), c[i] ) );
       }
   }
   void addAlias( std::string or, std::string nw ) {
       std::map<uint, keyWord>::iterator it = actions.find( hashStr( or ) );
       if( it == actions.end() ) { 
           std::cout << "Keyword " + or + " doesn't seem to exist!\n"; 
           return; 
       }
       actions.insert( std::make_pair( hashStr( nw ), it->second ) );
       std::cout << "Done\n";
   }
   bool parse( std::string a, command& cmd ) {
       std::transform( a.begin(), a.end(), a.begin(), ::tolower );
       std::istringstream iss( a ); 
       std::vector<std::string> vec;
       copy( std::istream_iterator<std::string>( iss ), std::istream_iterator<std::string>(), std::back_inserter<std::vector<std::string> >( vec ) );
       std::map<uint, keyWord>::iterator it = actions.find( hashStr( vec[0] ) );
       if( it == actions.end() ) { 
           std::cout << "'" + vec[0] + "' doesn't make any sense!\n"; 
           return false; 
       }
       cmd.action = it->second; 
       if( cmd.action < attack ) return  true;
       if( cmd.action < name ) {
           if( vec.size() < 2 ) { 
               std::cout << "What should I '" + names[cmd.action] + "'?\n"; 
               return false; 
           }
           if( !vec[1].compare( "coin" ) || !vec[1].compare( "coins" ) ) vec[1] = "gold";
           it = actions.find( hashStr( vec[1] ) );
           if( it == actions.end() ) { 
               std::cout << "I don't know what '" + vec[1] + "' means.\n"; 
               return false; 
           }
           cmd.subKey = it->second;
           if( cmd.action == alias ) {
               if( vec.size() < 3 ) { 
                   std::cout << "Aren't you forgetting something?\n"; 
                   return false; 
               }
               std::map<uint, keyWord>::iterator it = actions.find( hashStr( vec[2] ) );
               if( it != actions.end() ) { 
                   std::cout << "'" + vec[2] + "' is already a keyword!\n"; 
                   return false; 
               }
               if( cmd.subKey > name ) { 
                   std::cout << "No alias for '" + vec[1] + "' is allowed!\n"; 
                   return false; 
               }
               cmd.str1 = vec[1]; 
               cmd.str2 = vec[2];
           }
           return true;
       }
       if( cmd.action == name ) { 
           if( vec.size() < 2 ) { 
               std::cout << "What name?\n"; 
               return false; 
           }
           cmd.str1 = vec[1]; 
           return true; 
       }
       std::cout << "I can't understand '" + vec[0] + "'!\n"; 
       return false;
   }

private:

   unsigned hashStr( std::string s ) {
       unsigned wrd = 0x4e67c6a7, p = 0;
       while( p < s.length() ) 
           wrd ^= ( ( wrd << 5 ) + s[p++] + ( wrd >> 2 ) );
       return wrd;
   }
   std::map<uint, keyWord> actions;
   std::map<keyWord, std::string> names;

};

class treasure { public:

   treasure( keyWord k, uint c ) : key(k), cnt(c) { }
   keyWord key;
   uint cnt;

};

class box { public:

   void display() {
       if( !stuff.size() ) { 
           std::cout << objectName[0] + ".\n\n"; 
           return; 
       }
       std::ostringstream oss;
       for( std::map<keyWord, uint>::iterator it = stuff.begin(); it != stuff.end(); it++ ) {
           if( it->second > 0 ) {
               oss << " + " << it->second << " " << objectName[it->first - nothing]; std::cout << oss.str();
               if( it->first == gold ) std::cout << ( it->second > 1 ? " coins" : " coin" );
               else std::cout << ( it->second > 1 ? "s" : "" );
               std::cout << "\n"; 
               oss.str( "" );
           }
       }
       std::cout << "\n";
   }
   void stow( std::vector<treasure> t ) {
       for( std::vector<treasure>::iterator i = t.begin(); i != t.end(); i++ ) {
           std::map<keyWord, uint>::iterator it = stuff.find( ( *i ).key );
           if( it == stuff.end() ) stuff.insert( std::make_pair( ( *i ).key, ( *i ).cnt ) ); 
           else it->second += ( *i ).cnt;
       }
   }
   std::vector<treasure> dump( keyWord k, uint c = 0 ) {
       std::vector<treasure> t;
       if( k == all ) {
           for( std::map<keyWord, unsigned>::iterator it = stuff.begin(); it != stuff.end(); it++ ) {
               t.push_back( treasure( ( *it ).first, ( *it ).second ) );
           }
           stuff.clear();
       }
       else {
           std::map<keyWord, unsigned>::iterator it = stuff.find( k );
           if( it == stuff.end() ) return t;
           uint z = ( it->second ); 
           c = k == ladder ? 1 : !c ? z : c;
           it->second -= c; 
           if( !it->second ) stuff.erase( it );
           t.push_back( treasure( k, c ) );
       }
       return t;
   }
   size_t size() {
       return stuff.size();
   }
   uint count( keyWord k ) {
       std::map<keyWord, unsigned>::iterator it = stuff.find( k );
       if( it != stuff.end() ) return ( *it ).second;
       return 0;
   }

private:

   std::map<keyWord, uint> stuff;

};

class room { public:

   room( std::string n ) : name(n), visited(false) {
       memset( bRooms, 0, sizeof( bRooms ) ); 
       memset( ladderZ, 0, sizeof( ladderZ ) ); 
   }
   void describe() {
       std::cout << "\n\t** Room: " + name + " **\n\n"; 
       std::vector<std::string> ex; 
       getExits( ex );
       if( !ex.size() ) std::cout << "There are no exits from this room.\n";
       else {
           std::cout << "From here, you can go ";
           for( std::vector<std::string>::iterator x = ex.begin(); x != ex.end(); x++ ) {
               std::cout << *x + " ";
           }
           std::cout << "\n";
       }
       if( loot.size() ) {
           std::cout << "Here you can see:\n"; 
           loot.display();
           return;
       }
       std::cout << "You see nothing useful here.\n"; 
   }
   room* atk( keyWord d, uint lvl ) {
       if( bRooms[d] ) return 0;
       room* r = new room( "no name" ); 
       bRooms[d] = r->bRooms[inverted[d]] = true;
       r->addLoot( false, lvl );
       return r;
   }
   void addLoot( bool forceSledge, uint lvl ) {
       std::vector<treasure> t;
       if( forceSledge ) t.push_back( treasure( sledge, 1 ) );
       else if( rand() % 10 > 6 )  t.push_back( treasure( sledge, 1 ) );
       if( rand() % 10 > 6 ) t.push_back( treasure( gold, rand() % 8 + 1 ) );
       if( rand() % 10 == 2 || ( !ladderZ[lvl] && rand() % 10 < 5 ) ) {
           t.push_back( treasure( ladder, 1 ) ); 
           ladderZ[lvl] = true;
       }
       loot.stow( t );
   }
   void setName( std::string n ) {
       name = n;
       std::cout << "OK\n";
   }
   std::vector<treasure> tak( keyWord k ) {
       return loot.dump( k );
   }
   void drp( std::vector<treasure> t ) {
       loot.stow( t );
   }
   uint count( keyWord k ) {
       return loot.count( k );
   }
   void setVisited() {
       visited = true;
       lastTime = time( 0 );
   }
   bool wasVisited() {
       return visited;
   }
   time_t lastTimeVisited() {
       return lastTime;
   }
   void getExits( std::vector<std::string>& ex ) {
       for( uint x = 0; x < MAX_DOORS; x++ )
           if( bRooms[x] )
               ex.push_back( directions[x] );
   }

private:

   std::string    name;
   box loot;
   time_t lastTime;
   bool bRooms[MAX_DOORS], ladderZ[MX_R], visited;

};

class player { public:

   player() : curRoom(0) { }
   ~player() {
       deleteRooms();
   }
   void init() {
       equipped = nothing; deleteRooms();
       curRoom = new room( "Where it all begins" ); 
       curRoom->setVisited(); 
       curRoom->addLoot( true, pos.z );
       do pos.set( rand() % MX_R, rand() % MX_R, rand() % MX_R );
       while( pos.x == 8 && pos.y == 8 && pos.z == 8 ); 
       cave.insert( std::make_pair( pos.x + pos.y * MX_R + MX_R * MX_R * pos.z, curRoom ) );
       lok();
   }
   void lok() {
       curRoom->describe();
       if( equipped > nothing ) std::cout << "You are equipped with a " << objectName[equipped - nothing] << "\n";
   }
   void une() {
       if( equipped == nothing ) { 
           std::cout << "You are equipped with nothing!\n"; 
           return; 
       }
       equipped = nothing;
       std::cout << "Done\n";
   }
   void inv() {
       std::cout << "You are carrying"; 
       std::cout << ( !loot.size() ? " " : ":\n" );
       loot.display();
   }
   void tak( keyWord k ) {
       if( k < gold && k != all ) { 
           std::cout << "I don't know how to do that!\n"; 
           return; 
       }
       if( k == ladder && loot.count( k ) ) { 
           std::cout << "These things are so heavy, you can't carry more than one!\n"; 
           return; 
       }
       std::vector<treasure> t = curRoom->tak( k );
       if( !t.size() ) {
           if( k == all ) std::cout << "There is nothing here to take!\n";
           else std::cout << "I can't take what's not here!\n";
           return;
       }
       loot.stow( t );
       if( k == all ) {
           uint lc = loot.count( ladder );
           while( lc > 1 ) {
               drp( ladder, true );
               lc--;
           }
       }
       std::cout << "Taken\n";
   }
   void equ( keyWord k ) {
       if( k < sledge ) { 
           std::cout << "I don't know how to equip this!\n"; 
           return; 
       }
       if( equipped != nothing ) return;
       if( !loot.count( k ) ) { 
           std::cout << "You are not carrying one of those."; 
           return; 
       }
       equipped = k;
       std::cout << "OK\n";
   }
   bool atk( keyWord k ) {
       if( equipped == nothing ) { 
           std::cout << "I cannot let you hurt yourself!\n"; 
           return true; 
       }
       if( checkBoundaries( k ) ) { 
           std::cout << "The rocks here are too hard, you broke your sledge!\n"; 
           equipped = nothing; 
           std::vector<std::string> ex;
           curRoom->getExits( ex );
           if( !ex.size() ) {
               std::cout << "\nUnfortunately you are trapped in here ---- FOREVER!\n\n"
                            "\t*** G A M E * O V E R ***\n\n";
               return false;
           }
           return true; 
       }
       uint lvl = pos.z + k == up ? -1 : k == down ? 1 : 0;
       room* r = curRoom->atk( k, lvl );
       if( !r ) { 
           std::cout << "There is already a doorway there!\n"; 
           return true; 
       }
       position p = pos + dirVec[k]; 
       cave.insert( std::make_pair( p.x + MX_R * p.y + MX_R * MX_R * p.z, r ) );
       std::cout << "KA-POW!\n";
   }
   void mov( keyWord d ) {
       if( d > down ) { 
           std::cout << "I don't know how to move in that direction!\n"; 
           return; 
       }
       if( d == up && loot.count( ladder ) ) { 
           std::cout << "You are not strong enough to climb up there carrying a ladder!\n"; 
           return; 
       }
       position p = pos + dirVec[d];
       std::map<uint, room*>::iterator it = cave.find( p.x + MX_R * p.y + MX_R * MX_R * p.z );
       if( it == cave.end() ) {
           std::cout << "There is a "; 
           if( d == up ) std::cout << "ceiling";
           else if( d == down ) std::cout << "floor"; 
           else std::cout << "wall";
           std::cout << " blocking your way!\n\n"; 
       }
       else {
           curRoom = it->second; 
           pos = p;
           if( d == up && !curRoom->count( ladder ) ) { 
               std::cout << "I didn't know you could fly!\n"; 
               return; 
           }
           if( curRoom->wasVisited() ) {
               if( rand() % 10 > 5 && difftime( time( 0 ), curRoom->lastTimeVisited() ) > 180 ) curRoom->addLoot( false, pos.z );
           }
           curRoom->setVisited();
       }
   }
   void drp( keyWord k, bool silence = false ) {
       curRoom->drp( loot.dump( k ) );
       if( !silence ) std::cout << "Dropped\n"; 
   }
   room* currentRoom() {
       return curRoom;
   }
   position getPos() {
       return pos;
   }

private:

   void deleteRooms() {
       for( std::map<uint, room*>::iterator it = cave.begin(); it != cave.end(); it++ )
           delete it->second;
       cave.clear();
   }
   bool checkBoundaries( keyWord k ) {
       return k == up && pos.z - 1 < 0 || k == down && pos.z + 1 >= MX_R || k == west && pos.x - 1 < 0 || 
              k == east && pos.x + 1 >= MX_R || k == north && pos.y - 1 < 0 || k == south && pos.y + 1 >= MX_R;
   }
   room* curRoom;
   box loot;
   position pos;
   keyWord equipped;
   std::map<uint, room*> cave;

};

class game { public:

   game() {
       instance = this; 
       goal.set( 3, 3, 3 );
       callee c[] = { mov, mov, mov, mov, mov, mov, inv, une, lok, hlp, qit, atk, drp, tak, eqp, als, nam, 0, 0, 0, 0 };
       for( uint i = 0; i < sizeof( c ) / sizeof( c[0] ); i++ )
           functions.insert( std::make_pair( static_cast<keyWord>( i ), c[i] ) );
   }
   void play() {
       command cmdLine; 
       std::string a;
       while( true ) {
           initGame();
           while( !gameOver ) {
               std::cout << "\n>"; 
               std::getline( std::cin, a ); 
               if( !cmdParser.parse( a, cmdLine ) ) continue;
               std::map<keyWord, callee>::iterator it = functions.find( cmdLine.action );
               if( it == functions.end() ) { 
                   std::cout << "What do you mean?"; 
                   continue; 
               }
               it->second( cmdLine );
           }
           if( playerQuit ) return;
           std::cout << "Play again (Y/N)?\n>";
           std::getline( std::cin, a ); 
           if( a[0] != 'y' && a[0] != 'Y' ) return;
       }
   }

private:

   void checkWin() {
       if( plr.getPos() == goal ) {
           std::cout << "\nYou have found the ** TREASURE ROOOM **\n\n"
               "\nAll around you are thousands and thousands of piles of gold\n\n ** and they are all yours! ** \n"
               "\n\nCONGRATULATIONS!!!\n\n\n\n";
           gameOver = true; 
           playerQuit = false;
           return;
       }
       plr.currentRoom()->describe();
   }
   void initGame() {
       gameOver = false; 
       playerQuit = true;
       plr.init();
   }
   void showHelp() {
       std::cout << "\n ** Welcome Dungeon Explorer **\n\nDig your way to Room 8, 8, 8! (...but where is it?)\n\n"
                    "In your journey you can use following commands:\n\n* north, south, east, west, up and down: move in that direction\n"
                    "* attack <direction>: if equipped with sledge, it will open a passage\n  in that direction\n"
                    "* drop <object>: drops the object in the room you are\n* take <object>: takes a object from room into you inventory\n"
                    "* inventory: list all things you are carrying\n* look: describes the room you are in\n""* equip <object>: equip you "
                    "with the object\n* unequip: the opposite of equip\n* name <new name>: rename the room you are in\n"
                    "* alias <command> <new name>: creates a alias for the command\n* quit: terminates the game\n* help: show this long text...\n\n"
                    "  ** To go upwards, you need a ladder! **\n\n";
   }
   static void atk( command c ) { 
       if( !instance->plr.atk( c.subKey ) ) {
           instance->gameOver = true;
           instance->playerQuit = false;
       }
   }
   static void mov( command c ) { instance->plr.mov( c.action ); instance->checkWin(); }
   static void inv( command c ) { instance->plr.inv(); }
   static void une( command c ) { instance->plr.une(); }
   static void lok( command c ) { instance->plr.lok(); }
   static void drp( command c ) { instance->plr.drp( c.subKey ); }
   static void tak( command c ) { instance->plr.tak( c.subKey ); }
   static void eqp( command c ) { instance->plr.equ( c.subKey ); }
   static void nam( command c ) { instance->plr.currentRoom()->setName( c.str1 ); }
   static void als( command c ) { instance->cmdParser.addAlias( c.str1, c.str2 ); }
   static void hlp( command c ) { instance->showHelp(); }
   static void qit( command c ) { instance->gameOver = true; }
   bool gameOver, playerQuit;
   std::map<keyWord, callee> functions;
   parser cmdParser;
   player plr;
   position goal;
   static game* instance;

};

game* game::instance = 0; int main( int argc, char* argv[] ) {

   srand( static_cast<uint>( time( NULL ) ) );
   game g; g.play();
   return 0;

} </lang>