Text adventure user input
Overall, this is structured a little non-ideally.
Consider instead a more generic data-driven structure. Do not have variables like `t1` or so on for each room. Instead, have a map of rooms. I'm not super familiar with Java, but in C# that would be something like:
Dictionary<string, Room> rooms = new Dictionary<string, Room>();
// replace the below with a data file you read out of instead of hard-coding the rooms
rooms.Add("t1", new Room( /* title, description, exits, etc. */ ));
rooms.Add("t2", new Room( /* title, description, exits, etc. */ ));
rooms.Add("t3", new Room( /* title, description, exits, etc. */ ));
You can now have a simplified input loop that simply reads commands and moves through exits (or does other things) as desired.Room currentRoom = rooms["t1"];
while (game_is_running) {
string commandLine = ReadInput();
if (currentRoom.Exits.ContainsKey(commandLine)) {
currentRoom = currentRoom.Exits[commandLine];
DisplayRoom(currentRoom);
// explained below
EmitEvent(new VisitedRoomEvent(currentRoom));
}
}
void DisplayRoom(Room room) {
System.Out.PrintLine(string.Format("[{0}]", room.Title));
System.Out.PrintLine(room.Description);
for (Exit exit : room.Exits)
System.Out.PrintLine(string.Format("You can go {0}.", exit.Name));
}
For game logic like your exit condition after the player visits t2 and t3, you can place this in a separate game logic object that responds to events. If you emit events for things like "VisitedRoom" this other object will listen for those events and set its internal logic as appropriate.class YourLogic : EventHandler {
bool VisitedT1;
bool VisitedT2;
virtual void OnVisitedRoom(VisitedRoomEvent ev) {
if (ev.Room.name == "t1")
VisitedT1 = true;
if (ev.Room.name == "t2")
VisitedT2 = true;
if (VisitedT1 && VisistedT2)
game_is_running = false;
}
}
Overall, logic like the above isn't all that great. You might consider a more abstract puzzle/quest system that tracks state and allows you to define objectives in data rather than hard-coding them into the application. This is non-trivial, though. You could also consider using a scripting language so that you can change around game logic without having to recompile the adventure engine.
You say each room has a vector of exits. I assume your data structures look like this (minimally) then?
class Character
{
Room currentRoom;
}
//...
class Room
{
Vector<Room> exits;
}
If so, you could write a method like so:
//in character class...
void takeExit(string cmd)
{
for(Room room : currentRoom.exits)
{
if cmd.equalsIgnoreCase(room.name)
{
this.room = room;
return;
}
}
//didn't find a room.
System.out.println("Sorry, I didn't understand.");
}
It's not ideal for a number of complicated reasons, but from a beginner standpoint it's more than enough.
Also, you should probably be using an ArrayList rather than a vector if possible.
@SeraphLance: I tried placing that method in the Character class. It doesn't seem to recognize any of my commands.
I created a Character called test.
I made a scanner called com and called the method in main like this:
test.takeExit(com.nextLine());
Am I doing something wrong?
OH, I see. I forgot to mention that I had an exit class. Each room has a map of exits
Map<String, Room> map = new HashMap<String, Room>();
I took this idea from SeanMiddleditch.
It's probably worthwhile to learn to use a debugger for things like this, especially where user input is involved. Are you using an IDE like Eclipse? It has a fantastic debugger available that you can read about elsewhere, and virtually all IDEs have debuggers of some kind.
I still think you are going about it the wrong way.
I believe you should parse the input immediately after receiving it into individual works. then, you should pass that command to a generic command processor that handles all the normal commands:
n, s, e, w (directions), l (look), i (inventory), h (help), and q (quit).
If the generic processing fails, then call the rooms processing function to check for room specific commands:
(I don't know java syntax well, but hopefully it make sense)
// assume we have a Player class which keeps stats, inventory, etc
Player player("Mark");
// we'll start in the storage room
Room currentRoom = Rooms["storageRoom"];
currentRoom.PrintDescription();
while (game_is_running) {
string commandLine = ReadInput();
// if generic processing doesn't handle the command, do room specific
if (!GenericProcessing(currentRoom, player, commandLine)) {
// if the room specific processing fails, then it's invalid command
if (!currentRoom.Processing(currentRoom, player, commandLine)) {
print("Invalid command " + commandLine);
}
}
}
// assumes currentRoom is an in/out variable (passed by reference)
boolean GenericProcessing(Room currentRoom, Player player, string commandLine)
{
// if this is a direction and an exit exists...
if (currentRoom.IsExit(commandLine) {
// change to that room
currentRoom = currentRoom.Exit(commandLine);
currentRoom.PrintDescription();
return true;
}
// check for look
if (commandLine == "l" || commandLine == "look") {
currentRoom.PrintDescription();
return true;
}
// do the same for i, h, q, any other generic command
return false;
}