⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

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++


#include <stdio.h>
#include <time.h>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#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;
}