Text adventure user input

Started by
5 comments, last by BeerNutts 9 years, 9 months ago
I am trying to allow the player to travel to different rooms. Each room (t1, t2, and t3) has a vector of exits. When I run the program, the player is able to travel to a room. However, it automatically places the user back in the starter room. Is there a better way that i should be checking this?
Character test = new Character ("Test", "Sample character", t1, null);
test.printCharacter();
t1.printLocation();
Scanner input = new Scanner(System.in);
test.setCommand(input.nextLine());
//Options for t1
if (test.getCommand().equals(t1.getExits().get(0).getDirName())) {
test.setLocation(t2);
t2.printLocation();
t2.visited = true;
}
if (test.getCommand().equals(t1.getExits().get(1).getDirection())) {
test.setLocation(t3);
t3.printLocation();
t3.visited = true;
}
while (test.getLocation() != t1){
//Options for t2
if (test.getCommand().equals(t2.getExits().get(0).getDirection())) {
test.setLocation(t1);
t1.printLocation();
}
//Options for t3
if(test.getCommand().equals(t3.getExits().get(0).getDirName())) {
test.setLocation(t1);
t1.printLocation();
}
}
if ((t2.visited == true) && (t3.visited)) {
input.close();
}
}
Advertisement
I'm having some trouble really following the logic here, htough I'm sure it's a simple error somewhere.

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.

Sean Middleditch – Game Systems Engineer – Join my team!

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;
}

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

This topic is closed to new replies.

Advertisement