RCRPG/C++98
Appearance
< RCRPG
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.
#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;
}