After having a think about some of the theory and practicalities of what I’d need to be doing to get doors into our little text adventure, I just went ahead and did it, so here we are! Time to get back into the code and get doors into our game.
We usually start with a little housekeeping and tidying of our code but not today. Today we’re gonna jump right into coding the door itself. The housekeeping will have to come later after the door code has broken everything else!
Just a quick recap of how doors are going to work. They’re going to replace the destinations on the map, so instead of going south to the dungeon, we’re going to go south to the door and work out what needs to happen from there.
So we’ll dive into our properties file and create a struct for the door and of course set up our enumerators. Our enum looks like this:
enum en_Doors {CELLDOOR, MAX_DOORS};
Ok, so there’s only one door in there, but that’s just for now, we can easily add as many doors as we like as we need them. Next is the struct itself, I won’t go through it as that was discussed in part 1.
struct door
{
string description_1_open;
string description_1_closed;
string description_2_open;
string description_2_closed;
string on_examination_1;
string on_examination_2;
int location_1;
int location_2;
bool isopen;
bool islocked;
string word;
int code;
};
We’re done in properties, lets head to the rooms file and set up our door there. You can if you want set up a new file just for doors and include it the way we have the rest but for the purposes of this tutorial and keeping thing simple I’m just going to go ahead and put it in the rooms file. This is a very straight forward process you’ve done before so I’m not going to talk through that either.
inline void set_doors(door *drs)
{
drs[CELLDOOR].description_1_closed = “a closed CELLDOOR that leads to the dungeon”;
drs[CELLDOOR].description_1_open = “an open CELLDOOR, it leads to the dungeon”;
drs[CELLDOOR].description_2_closed = “a closed CELLDOOR that leads into the cell”;
drs[CELLDOOR].description_2_open = “an open CELLDOOR that leads to the cell”;
drs[CELLDOOR].on_examination_1 = “a rusty but solid CELLDOOR, the lock is on the other side, you can see a key through the bars”;
drs[CELLDOOR].on_examination_2 = “a rusty but solid CELLDOOR, the cell beyond looks pretty filthy”;
drs[CELLDOOR].location_1 = CELL;
drs[CELLDOOR].location_2 = DUNGEON;
drs[CELLDOOR].isopen = false;
drs[CELLDOOR].islocked = true;
drs[CELLDOOR].word = “CELLDOOR”;
drs[CELLDOOR].code = CELLDOOR;
}
The other thing we’re going to have to do is actually put the door on the map, since it’s a cell door that leads to the dungeon, lets put it there!
inline void set_rooms(room *rms)
{
rms[CELL].description.assign(“dark and musty cell with no windows”);
rms[CELL].exits_to_room[NORTH] = NONE;
rms[CELL].exits_to_room[SOUTH] = CELLDOOR;
rms[CELL].exits_to_room[EAST] = NONE;
rms[CELL].exits_to_room[WEST] = NONE;
rms[DUNGEON].description.assign(“cold and dirty dungeon”);
rms[DUNGEON].exits_to_room[NORTH] = CELLDOOR;
rms[DUNGEON].exits_to_room[SOUTH] = NONE;
rms[DUNGEON].exits_to_room[EAST] = HALLWAY;
rms[DUNGEON].exits_to_room[WEST] = NONE;
….
}
We’re done with rooms, time to head over to our main file and get it set up, and then the real fun begins. In our main loop set up the door as we did the other structs.
door doors[MAX_DOORS];
set_doors(doors);
And of course we need to add doors to our parser call so we have:
parser(location, rooms, directions, verbs, nouns, words, doors);
and matching in the function:
bool parser (int &loc, room *rms, verb *dir, verb *vbs, noun *nns, vector<string> words, door *drs)
So that’s all the basic leg work done. Now to dig into the parser and get this working in the game! Go to the parser function. First thing we need to do is check if the player has entered any doors and add them to the stepper and the commend vectors so we can handle them, otherwise any call to the door would go ignored.
//check for doors
for(j = 0; j < MAX_DOORS; j++)
{
if(words[i] == drs[j].word)
{
stepper.push_back(drs[j].code);
commander.push_back(“DOOR”);
}
}
So now, whenever someone wants to use a door the code is stored to the stepper and we know the code is for a door from the commander. We’re another step closer to having out door and eating them (if that’s what you’re into, I won’t judge).
So now that the parser understands what a door is we need to tell it what to do with them. Since we check for the other word types we need to add another conditional to each command to catch that. We’ll put it before the verb check as that’s always last, so that we can move on if the player used two verbs in a row. So we check direction, noun, door, verb in that order. We’ll start with the easy commands and move on from there. The inventory code will now look like this:
if(stepper[i] == INV)
{
//Check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
inventory(nns);
cout << “Did you mean GO ” + dir[stepper[i+j+1]].word + “?\n”;
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
if(nns[stepper[i+j+1]].location == POCKET)
{
cout << “I have a ” + nns[stepper[i+j+1]].word + ” in my inventory.\n”;
}
else if(nns[stepper[i+j+1]].location == loc)
{
cout << “I don’t have a ” + nns[stepper[i+j+1]].word + ” but there’s one in the room.\n”;
}
else
{
cout << “I don’t have a ” + nns[stepper[i+j+1]].word + “.\n”;
}
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “That’s not in any kind of inventory, except maybe the castle blueprints.\n”;
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
inventory(nns);
}
}
//if there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
inventory(nns);
}
}
We just simply tell the player they don’t and couldn’t have a door. Remeber it’s important to have the door conditional in the right place. It’s the same for drop:
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “You can’t drop what you couldn’t get in the first place.\n”;
}
and Get:
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “You can’t get that.\n”;
}
Now for the more complicated stuff, Look and Go. We’ll take care of Look first which is a little more complicated but worth doing first. We’ll need to pass the door struct to the look function so do that:
bool look(int loc, room *rms, noun *nns, verb *dir, door *drs)
And amend any function calls in the code otherwise it won’t compile, also we want to take advantage of the new information.
look(loc, rms, nns, dir, drs);
Now that we’re adding doors as exit locations instead of dungeon rooms we need to check that. So we’ll add a bool isdoor and call it fasle at the start, and if we do come across a door we’ll change it to true and only print the room description if there is no door. Saves the look function from printing 2 descriptions for a direction if there is a door that way. Of course it wouldn’t know what was in that direction and would end up spouting out something random.
Even though we only have one door at the moment we might have more in the future so we’re going to future proof by using a loop now instead of having to change a load of code if we decide to add another door later. Simply:
for(int i = 0; i < MAX_DIRS; i++)
{
//flag to make sure we don’t output twice by accident
isdoor = false;
//Check that there’s something at this direction
if(rms[loc].exits_to_room[i] != NONE)
{
//Check that if it’s a door rather than a exit at a direction
for(int j = 0; j < MAX_DOORS; j++)
{
So we’re checking each direction and now running through the list of doors it’s time to check if there actually is a door and set that bool
//Check if it is in fact a door
if(rms[loc].exits_to_room[i] == drs[j].code)
{
//change flag to avoid doubling up now we know it’s a room
isdoor = true;
Ok, so we’ve found ourselves a door. Next we’re going to check it’s location against ours so we know what description to use, then check whether or not the door is open and or locked and give the appropriate output and since this is the same for both locations there’s a little copy and pasting going on here with a few tweaks and then close the loop:
//check which room location we’re at
if(loc == drs[j].location_1)
{
if(!drs[j].isopen)
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_1_closed + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
else
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_1_open + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
}
else
{
if(!drs[j].isopen)
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_2_closed + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
else
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_2_open + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
}
}
}
Great, all we need to do now is check if it wasn’t a door and print out the room description that’s in that direction and close out the enclosing loop and output the results:
//It’s not a door so it must be a direction to a room
if(!isdoor)
{
str = str + “To the ” + dir[i].word + ” is a ” + rms[rms[loc].exits_to_room[i]].description + “. “;
}
}
}
And we’re done in the Look function. That wasn’t so bad eh. We need to give the same treatment as above in the look function itself in case a player want’s to look directly at the door itself. Its a simple copy paste job more or less and looks like so:
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
//Check which door location we’re at so we can respond appropriately
if(loc = drs[stepper[i+j+1]].location_1)
{
if(drs[stepper[i+j+1]].isopen)
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_open + “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_open + “, and it is unlocked.\n”;
}
}
else
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_closed+ “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_closed+ “, and it is unlocked.\n”;
}
}
}
else
{
if(drs[stepper[i+j+1]].isopen)
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_open + “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_open + “, and it is unlocked.\n”;
}
}
else
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_closed+ “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_closed+ “, and it is unlocked.\n”;
}
}
}
}
Last but not least is sorting out Go. Now that we no longer have a destination on the map we have to sort out the logic of the player telling the parser they want to go to a door. If the player wants to go through a door its a simple process of checking what the doors other location is and putting the player in there:
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
if(!drs[stepper[i+j+1]].islocked)
{
if(drs[stepper[i+j+1]].isopen)
{
if(loc == drs[stepper[i+j+1]].location_1)
{
loc = drs[stepper[i+j+1]].location_2;
look(loc, rms, nns, dir, drs);
}
else
{
loc = drs[stepper[i+j+1]].location_1;
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is closed.\n”;
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is locked.\n”;
}
}
If the player want to go in a direction the door is the parser doesn’t have a destination so it will have to check the door for that, similar to the above:
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
//Check there’s somewhere to actually go
if(rms[loc].exits_to_room[stepper[i+j+1]] != NONE)
{
bool isdoor = false;
for(int j = 0; j < MAX_DOORS; j++)
{
if(rms[loc].exits_to_room[i] == drs[j].code)
{
isdoor = true;
if(!drs[stepper[i+j+1]].islocked)
{
if(drs[stepper[i+j+1]].isopen)
{
if(loc == drs[stepper[i+j+1]].location_1)
{
loc = drs[stepper[i+j+1]].location_2;
look(loc, rms, nns, dir, drs);
}
else
{
loc = drs[stepper[i+j+1]].location_1;
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is closed.\n”;
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is locked.\n”;
}
}
}
if(!isdoor)
{
loc = rms[loc].exits_to_room[stepper[i+j+1]];
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “There is no exit that way.\n”;
}
}
They all follow more or less the same logic but you need to be careful not to miss any of it! So now we have doors in the code and a door in, the great barriers of games! In time we’ll use them to confuse and maybe frustrate players a little until they figure out how to open them.
As always, if you have any question or queries feel free to post a comment or reach out to me on twitter (@SeveralBytes). Thanks for reading and I hope you get something from this!
Here is all the code for the parser after today’s additions
properties.cpp
#ifndef __PROPERTIES_H_INCLUDED___
#define __PROPERTIES_H_INCLUDED___
#include <string>
#include <vector>
using namespace std;
enum en_DIRS {NORTH, SOUTH, EAST, WEST, MAX_DIRS};
enum en_ROOMS {CELL, DUNGEON, HALLWAY, KEEP, GATE, FREEDOM, POCKET, MAX_ROOMS};
enum en_VERBS {GO, LOOK, GET, DROP, INV, MAX_VERBS};
enum en_NOUNS {STICK, STRING, WIRE, KEY, MAX_NOUNS};
enum en_Doors {CELLDOOR, MAX_DOORS};
const int NONE = -1;
struct verb
{
string word;
vector<string> synonyms;
int code;
};
struct noun
{
string word;
string description;
string on_examination;
int code;
int location;
bool can_carry;
};
struct room
{
string description;
int exits_to_room[MAX_DIRS];
};
struct door
{
string description_1_open;
string description_1_closed;
string description_2_open;
string description_2_closed;
string on_examination_1;
string on_examination_2;
int location_1;
int location_2;
bool isopen;
bool islocked;
string word;
int code;
};
#endif //__PROPERTIES_H_INCLUDED___
rooms.cpp
#ifndef __ROOMS_H_INCLUDED__
#define __ROOMS_H_INCLUDED__
#include “properties.cpp”
inline void set_doors(door *drs)
{
drs[CELLDOOR].description_1_closed = “a closed CELLDOOR that leads to the dungeon”;
drs[CELLDOOR].description_1_open = “an open CELLDOOR, it leads to the dungeon”;
drs[CELLDOOR].description_2_closed = “a closed CELLDOOR that leads into the cell”;
drs[CELLDOOR].description_2_open = “an open CELLDOOR that leads to the cell”;
drs[CELLDOOR].on_examination_1 = “a rusty but solid CELLDOOR, the lock is on the other side, you can see a key through the bars”;
drs[CELLDOOR].on_examination_2 = “a rusty but solid CELLDOOR, the cell beyond looks pretty filthy”;
drs[CELLDOOR].location_1 = CELL;
drs[CELLDOOR].location_2 = DUNGEON;
drs[CELLDOOR].isopen = false;
drs[CELLDOOR].islocked = true;
drs[CELLDOOR].word = “CELLDOOR”;
drs[CELLDOOR].code = CELLDOOR;
}
inline void set_rooms(room *rms)
{
rms[CELL].description.assign(“dark and musty cell with no windows”);
rms[CELL].exits_to_room[NORTH] = NONE;
rms[CELL].exits_to_room[SOUTH] = CELLDOOR;
rms[CELL].exits_to_room[EAST] = NONE;
rms[CELL].exits_to_room[WEST] = NONE;
rms[DUNGEON].description.assign(“cold and dirty dungeon”);
rms[DUNGEON].exits_to_room[NORTH] = CELLDOOR;
rms[DUNGEON].exits_to_room[SOUTH] = NONE;
rms[DUNGEON].exits_to_room[EAST] = HALLWAY;
rms[DUNGEON].exits_to_room[WEST] = NONE;
rms[HALLWAY].description.assign(“plain hallway”);
rms[HALLWAY].exits_to_room[NORTH] = NONE;
rms[HALLWAY].exits_to_room[SOUTH] = KEEP;
rms[HALLWAY].exits_to_room[EAST] = NONE;
rms[HALLWAY].exits_to_room[WEST] = DUNGEON;
rms[KEEP].description.assign(“well lit, neat and tidy keep”);
rms[KEEP].exits_to_room[NORTH] = HALLWAY;
rms[KEEP].exits_to_room[SOUTH] = NONE;
rms[KEEP].exits_to_room[EAST] = NONE;
rms[KEEP].exits_to_room[WEST] = GATE;
rms[GATE].description.assign(“gatehouse, freedom seems so close”);
rms[GATE].exits_to_room[NORTH] = NONE;
rms[GATE].exits_to_room[SOUTH] = FREEDOM;
rms[GATE].exits_to_room[EAST] = KEEP;
rms[GATE].exits_to_room[WEST] = NONE;
rms[FREEDOM].description.assign(“wonderfully free land of freeness”);
rms[FREEDOM].exits_to_room[NORTH] = GATE;
rms[FREEDOM].exits_to_room[SOUTH] = NONE;
rms[FREEDOM].exits_to_room[EAST] = NONE;
rms[FREEDOM].exits_to_room[WEST] = NONE;
}
#endif //__ROOMS_H_INCLUDED__
main.cpp
#include <iostream>
#include <string>
#include <vector>
#include “rooms.cpp”
#include “nouns.cpp”
#include “verbs.cpp”
using namespace std;
bool section(string userInput, vector<string> &words)
{
string subString;
if(!userInput.empty())
{
//Make everything upper case for easier handling
for(int i = 0; i <= static_cast<signed int>(userInput.length()-1); i++)
{
userInput[i] = toupper(userInput[i]);
}
//Split userInput into a string vector for even easier handling later
for(int i = 0; i <= static_cast<signed int>(userInput.length()-1); i++)
{
if(userInput[i] != ‘ ‘ && i <= static_cast<signed int>(userInput.length()-1))
{
subString += userInput[i];
}
if(userInput[i] == ‘ ‘ || i == static_cast<signed int>(userInput.length()-1))
{
words.push_back(subString);
subString.clear();
}
}
}
else
{
words.push_back(“LOOK”);
}
return true;
}
bool look(int loc, room *rms, noun *nns, verb *dir, door *drs)
{
string str = “I’m in a ” + rms[loc].description + “. “;
bool andadded = false;
bool isdoor = false;
int numObjects = 0;
for(int i = 0; i < MAX_NOUNS; i++)
{
if(nns[i].location == loc)
{
if(numObjects == 0)
{
str = str + “I see “;
}
numObjects++;
str = str + nns[i].description;
}
int ismore = 0;
int j;
for(j = i+1; j < MAX_NOUNS; j++)
{
if(nns[j].location == loc)
{
ismore++;
}
}
if(ismore == 1 && !andadded && numObjects > 1)
{
str = str + ” and “;
andadded = true;
}
if(ismore > 1)
{
str = str + “, “;
}
}
if(numObjects > 0)
{
str = str + “. “;
}
for(int i = 0; i < MAX_DIRS; i++)
{
//flag to make sure we don’t output twice by accident
isdoor = false;
//Check that there’s something at this direction
if(rms[loc].exits_to_room[i] != NONE)
{
//Check that if it’s a door rather than a exit at a direction
for(int j = 0; j < MAX_DOORS; j++)
{
//Check if it is in fact a door
if(rms[loc].exits_to_room[i] == drs[j].code)
{
//change flag to avoid doubling up now we know it’s a room
isdoor = true;
//check which room location we’re at
if(loc == drs[j].location_1)
{
if(!drs[j].isopen)
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_1_closed + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
else
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_1_open + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
}
else
{
if(!drs[j].isopen)
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_2_closed + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
else
{
str = str + “To the ” + dir[i].word + ” is ” + drs[rms[loc].exits_to_room[i]].description_2_open + “, “;
if(drs[j].islocked)
{
str = str + “it is locked. “;
}
else
{
str = str + “it is unlocked. “;
}
}
}
}
}
//It’s not a door so it must be a direction to a room
if(!isdoor)
{
str = str + “To the ” + dir[i].word + ” is a ” + rms[rms[loc].exits_to_room[i]].description + “. “;
}
}
}
str = str + “\n”;
cout << str;
return true;
}
bool inventory(noun *nns)
{
bool emptyPockets = true;
for(int i = 0; i < MAX_NOUNS; i++)
{
if(nns[i].location == POCKET)
{
cout << “I have a ” + nns[i].word + “.\n”;
emptyPockets = false;
}
}
if(emptyPockets)
{
cout << “I’m not carrying anything.\n”;
}
return true;
}
bool parser (int &loc, room *rms, verb *dir, verb *vbs, noun *nns, vector<string> words, door *drs)
{
//By using a vector we can have multiple actions in one input
vector<int> stepper;
vector<string> commander;
int i = 0;
int j = 0;
for(i = 0; i < static_cast<signed int>(words.size()); i++)
{
//check for verb
for(j = 0; j < MAX_VERBS; j++)
{
if(words[i] == vbs[j].word)
{
stepper.push_back(vbs[j].code);
commander.push_back(“VERB”);
}
}
//check for route command
for(j = 0; j < MAX_DIRS; j++)
{
if(words[i] == dir[j].word)
{
stepper.push_back(dir[j].code);
commander.push_back(“DIR”);
}
}
//check for nouns
for(j = 0; j < MAX_NOUNS; j++)
{
if(words[i] == nns[j].word)
{
stepper.push_back(nns[j].code);
commander.push_back(“NOUN”);
}
}
//check for doors
for(j = 0; j < MAX_DOORS; j++)
{
if(words[i] == drs[j].word)
{
stepper.push_back(drs[j].code);
commander.push_back(“DOOR”);
}
}
}
if(stepper.empty() && words[0] != “QUIT”)
{
cout << “That didn’t make any sense.\n”;
return true;
}
for(i = 0; i < static_cast<signed int>(stepper.size()); i++)
{
j = 0;
if(commander[i] == “VERB”)
{
for(j = 0; j+i < static_cast<signed int>(stepper.size()); j++)
{
if(stepper[i] == LOOK)
{
//check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
//Check there’s something to look at
if(rms[loc].exits_to_room[stepper[i+j+1]] != NONE)
{
cout << “I see a ” + rms[rms[loc].exits_to_room[stepper[i+j+1]]].description + “.\n”;
}
else
{
cout << “There is nothing to the ” + dir[stepper[i+j+1]].word + “.\n”;
}
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
//Check the object is actually in the same location
if(nns[stepper[i+j+1]].location == loc)
{
cout << “I see ” + nns[stepper[i+j+1]].description + “.\n”;
}
else
{
cout << “I don’t see one of those here”;
}
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
//Check which door location we’re at so we can respond appropriately
if(loc = drs[stepper[i+j+1]].location_1)
{
if(drs[stepper[i+j+1]].isopen)
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_open + “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_open + “, and it is unlocked.\n”;
}
}
else
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_closed+ “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_1_closed+ “, and it is unlocked.\n”;
}
}
}
else
{
if(drs[stepper[i+j+1]].isopen)
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_open + “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_open + “, and it is unlocked.\n”;
}
}
else
{
if(drs[stepper[i+j+1]].islocked)
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_closed+ “, it is locked.\n”;
}
else
{
cout << “I see ” + drs[stepper[i+j+1]].description_2_closed+ “, and it is unlocked.\n”;
}
}
}
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
look(loc, rms, nns, dir, drs);
}
}
//If there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
look(loc, rms, nns, dir, drs);
}
}
if(stepper[i] == GO)
{
//Check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
//Check there’s somewhere to actually go
if(rms[loc].exits_to_room[stepper[i+j+1]] != NONE)
{
bool isdoor = false;
for(int j = 0; j < MAX_DOORS; j++)
{
if(rms[loc].exits_to_room[i] == drs[j].code)
{
isdoor = true;
if(!drs[stepper[i+j+1]].islocked)
{
if(drs[stepper[i+j+1]].isopen)
{
if(loc == drs[stepper[i+j+1]].location_1)
{
loc = drs[stepper[i+j+1]].location_2;
look(loc, rms, nns, dir, drs);
}
else
{
loc = drs[stepper[i+j+1]].location_1;
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is closed.\n”;
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is locked.\n”;
}
}
}
if(!isdoor)
{
loc = rms[loc].exits_to_room[stepper[i+j+1]];
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “There is no exit that way.\n”;
}
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
cout << “You cannot go there.\n”;
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
if(!drs[stepper[i+j+1]].islocked)
{
if(drs[stepper[i+j+1]].isopen)
{
if(loc == drs[stepper[i+j+1]].location_1)
{
loc = drs[stepper[i+j+1]].location_2;
look(loc, rms, nns, dir, drs);
}
else
{
loc = drs[stepper[i+j+1]].location_1;
look(loc, rms, nns, dir, drs);
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is closed.\n”;
}
}
else
{
cout << “The ” + drs[stepper[i+j+1]].word + ” is locked.\n”;
}
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
cout << “Go where?\n”;
}
}
//if there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
cout << “Go where?\n”;
}
}
if(stepper[i] == GET)
{
//Check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
cout << “Did you mean GO ” + dir[stepper[i+j+1]].word + “?\n”;
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
if(nns[stepper[i+j+1]].can_carry == true)
{
if(nns[stepper[i+j+1]].location == loc)
{
nns[stepper[i+j+1]].location = POCKET;
cout << “I picked up ” + nns[stepper[i+j+1]].description + “.\n”;
}
else if(nns[stepper[i+j+1]].location == POCKET)
{
cout << “I already have ” + nns[stepper[i+j+1]].description + “.\n”;
}
else
{
cout << “There isn’t ” + nns[stepper[i+j+1]].description + ” here.\n”;
}
}
else
{
cout << “I can’t pick that up.\n”;
}
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “You can’t get that.\n”;
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
cout << “Get what?\n”;
}
}
//if there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
cout << “Get what?\n”;
}
}
if(stepper[i] == DROP)
{
//Check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
cout << “Drop what?\n”;
cout << “Did you mean to GO ” + dir[stepper[i+j+1]].word + “?\n”;
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
if(nns[stepper[i+j+1]].location == POCKET)
{
nns[stepper[i+j+1]].location = loc;
cout << “I dropped ” + nns[stepper[i+j+1]].description + “.\n”;
}
else
{
cout << “I can’t drop something I don’t have.\n”;
}
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “You can’t drop what you couldn’t get in the first place.\n”;
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
cout << “Drop what?\n”;
}
}
//if there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
cout << “Drop what?\n”;
}
}
if(stepper[i] == INV)
{
//Check if there’s another word in the stepper to use
if((i+j+1) < static_cast<signed int>(stepper.size()))
{
//The next word is a direction
if(commander[i+j+1] == “DIR”)
{
inventory(nns);
cout << “Did you mean GO ” + dir[stepper[i+j+1]].word + “?\n”;
}
//The next word is a noun
else if(commander[i+j+1] == “NOUN”)
{
if(nns[stepper[i+j+1]].location == POCKET)
{
cout << “I have a ” + nns[stepper[i+j+1]].word + ” in my inventory.\n”;
}
else if(nns[stepper[i+j+1]].location == loc)
{
cout << “I don’t have a ” + nns[stepper[i+j+1]].word + ” but there’s one in the room.\n”;
}
else
{
cout << “I don’t have a ” + nns[stepper[i+j+1]].word + “.\n”;
}
}
//The next word is a door
else if(commander[i+j+1] == “DOOR”)
{
cout << “That’s not in any kind of inventory, except maybe the castle blueprints.\n”;
}
//The next word is a verb and we haven’t done anything yet
else if(commander[i+j+1] == “VERB” && j == 0)
{
inventory(nns);
}
}
//if there’s no more words in stepper to use and we haven’t already done something
else if((i+j+1) == static_cast<signed int>(commander.size()) && j == 0)
{
inventory(nns);
}
}
//stop inner loop if the next word is a verb or you hit the end of stepper
if(i+j+1 < static_cast<signed int>(stepper.size()))
{
if(commander[i+j+1] == “VERB”)
{
break;
}
}
if(i+j+1 >= static_cast<signed int>(stepper.size()))
{
break;
}
}
}
i = i + j;
}
return true;
}
int main()
{
string userInput;
vector<string> words;
int location = CELL;
verb directions[MAX_DIRS];
set_directions(directions);
verb verbs[MAX_VERBS];
set_verbs(verbs);
room rooms[MAX_ROOMS];
set_rooms(rooms);
noun nouns[MAX_NOUNS];
set_nouns(nouns);
door doors[MAX_DOORS];
set_doors(doors);
do
{
words.clear();
getline(cin, userInput);
section(userInput, words);
parser(location, rooms, directions, verbs, nouns, words, doors);
}while(words[0] != “QUIT”);
}