Textadventure (Newb Questions)

Hello JGO-Community,

Currently I’m working my way through the “Introduction to Programming Using Java” by David J. Eck. As i proceed through the chapters I try to implement and use the new learned features in a simple textadventure (Zork-Style). As of now I have a “Area System” that gives information about the area you are in and a working “Dialogue System” that first shows the NPC-Speech and then gives the player multiple choice answers. I use a switch statement for that.

It basically looks like this:

public static void showDialogueNPC(int indexDialogue)
	{
		/* 
		 * DIALOG 1: "NPC"
		 * Case 1: IntroductionText
                 * Case 2: AngryReply
                 * Case 3: Annoyed
		 */
		switch(indexDialogue)
		{
		case 1:         // IntroductionText
		{
			System.out.println("blablabla"); // NPC Talks
			System.out.println("");
			System.out.println("1. blablabla"); // possible replies
			System.out.println("2. blablabla");
			System.out.println("> ");

                        // I use a loop here in the case the player types in 3 for example when there's only 2 possible answers so the game jumps
                        // back here and he can try again
                	while(true)   
			{
				playerDialogueReply = stdIn.nextInt();        // Use Scanner to get player's input
				
				if(playerDialogueReply == 1)
				{
					showDialogueNPC(2);			// switch to case 2
					break;                                      // stop loop
				}
				else
				{
					....    // other replies
				}
			}
			
			break;
		} // end of case 1
                case 2:
                .
                .
                .
                .
		default:
		{
			System.out.println("DEBUG");
		}
		} // end of switch
	} // end of showDialogueNPC 

I know it’s probably (well rather most presumably) not the best way to solve this, but as I said I try to use whatever I learn. Long story short: I have four questions at the moment.

  1. Would it be reasonable to make a new Class called for example “Speech” and only use it to store the different “NPC-Dialouge lines” in strings? So that I only have to call
System.out.println(Speech.DialogNPC)

for example. The reason why I don’t want to put it to the other variable declarations at the beginning of my Dialogue class is because I have lot’s of text and it kind of overwhelmes my code…

  1. At the moment I can only run this in eclipse. Is there a way to run this programm outside of eclipse without providing an actual GUI (for example starting it via console)? I guess this is a really noobish question but I’m not that far in the book and would like to show it to a friend of mine (because he can’t wait to criticise my lousy dialogues and area-descriptions :)).

  2. One problem I have with the Scanner is, that if the user types in a char where an int is awaited by the programm it crashes. I guess this is where try… catch would kick in but I can’t quite wrap my head around it yet… Is there any other way to get the user’s input?

  3. The last question is more of a general kind as I know how to use subroutines but I kind of don’t know WHEN to use them. Or in other words I have the feeling that I overuse them, trying to put every single bit of my programm in it’s own methods… I hope you understand what I mean. Is there a guideline or a “proper” way?

I haven’t planned too much fancy stuff. As I said I try to implement only new stuff I learn from the book.

Greetings, EatenByAGrue

  1. I would do that at least, modularity is a big thing in Object Oriented programming. You could do something as simple as have a class that just holds static references to each NPC’s dialog, or you could create a NPC super class and then have your NPC’s extend that class to get all of it’s talking methods. Something like this:

public class NPC
{
   protected String name;

   public NPC(String name)
   {
      this.name = name;
   }

   public void talk()
   {
      System.out.println("Hello my name is " + name);
   }
}

class Male extends NPC
{
   protected String gender;

   public Male(String name, String gender()
   {
      super(name);
      this.gender = gender;
   }

   public void talk()
   {
      System.out.println("Hello my name is " + name + ", I am a " + gender);
   }
}

  1. Yes there is a way to run Java applications from the console. Check out these tutorials to see how. http://download.oracle.com/javase/tutorial/getStarted/cupojava/index.html

  2. Try-catch statements aren’t terribly difficult once you get to know them. Here is a tutorial about them http://download.oracle.com/javase/tutorial/essential/exceptions/ and here is an example how how to catch the exception that Scanner throws when the wrong type of input is given:


Scanner in = new Scanner(System.in);
int num = -999;

while(num == -999)
{
   System.out.print("Please enter a number: ");
   try
   {
      num = in.nextInt();
   }
   catch(InputMismatchException ime) // InputMismatchException is thrown when the wrong type of input is given
   {
      System.out.println("That's not a number!\n");
      // Call in.next() to get rid of the input left behind
      // Input is left behind because in.nextInt() didn't actually read
      // the input due to the InputMismatchException being thrown
      in.next();
   }
}

System.out.println("The number you entered is: " + num);

  1. I tend to prefer to do this but that’s just my opinion, generally though you don’t want to do it TOO much because then it’s just excessive.

Be carefull, you are using a recursive method : At each answer, you are invoking showDialogueNPC within showDialogueNPC. So at each answer, you are allocation more and more memory.

You should create classes to store dialogs and answers. At some point, I should advise you to use file to store your dialogs. If you use classes it will more easy to change your code when need.

Actually, the input is read and when determined that it is not an integer, an exception is thrown. However, when you press ENTER after typing a char/integer, it also includes the \r\n (if on Windows) or \n (if on Unix). in.next() simply clears those remaining chars.

Oops my bad, I forgot nextInt() left behind the Enter key and that you have to call next() to get rid of it. I apologize, I haven’t used Scanner in a while.

Thank you for your support Z-Man, Bonbon-Chan and ra4king ! :slight_smile:

I made some changes according to your suggestions. First of all I created a new class in which I store the NPC-Textlines until I figure out how to read them out of a file (as Bonbon-Chan suggested). Then I made a new class called Reply that handles all the replies for the player, in which the method getPlayerReply() reads the users input after each NPC-Dialog. Here I also included the try - catch statement (the tutorial really helped Z-Man). I didn’t even know about the recursive Method problem but I think I have fixed it now - thanks Bonbon-Chan!

At the moment I couldn’t use your suggestion about the Super-Class Z-Man, but I think I’ll get to that in a few days.

Now it looks like this (I included only one of the “NPCs” - Ignore the awefully written german dialogues :D):

EDIT: Removed the code

It still isn’t pretty but it works for now. Looking forward to learning more about objects :slight_smile:
EDIT: I almost forgot to say thank you for the tutorial about the console. I managed to run it from the terminal of my mac… but only if I compiled it first in eclipse. I guess the problem is, that when I compile it using the command line I have to manually care about the packages, right? -> Note to me: Look into that! :slight_smile:

greetings, EatenByAGrue

P.S.: Is it okay to post that “much” code??

The easy way is to use Eclipse to create a jar file. This link explains it.


In the package explorer right click and select “export”, then select Java -> Jar file.
Once the jar is build you can run it from the command line: java -jar app.jar

You use an amfull number of switch/case. Must off them could be avoid using tables.
You use a lot of static where I should not use them.
You are still using recursion : getPlayerReply -> hauerPlayerReply -> getPlayerReply -> hauerPlayerReply -> …

As a template, I should start with something like (I put everything abstract to not put all the code ;)) :


public abstract class Dialogues
{
   public Dialogue getDialogue(int nb);
}

public abstract class Dialogue
{
   public String getText();
   public int getNumberOfReply();
   public Reply getReply(int nb);
}

public abstact class Reply
{
   public String getText();
   public int      getNextDialogue();
}

public class DialogueManager
{
    private Dialogues dialogues;

    public  DialogueManager(Dialogues dialogues)
   {
       this.dialogues = dialogues;
   }

   public void start()
  {
      int activeDialogue = 0;
      Scanner stdIn = new Scanner(System.in);
    

     while(activeDialogue >=0)
     {
         Dialogue dialogue = dialogues.getDialogue(activeDialogue);

         System.out.println(dialogue.getText());

         for(int i=0;i<dialogue.getNumberOfReply();i++)
         {
            System.out.println((i+1)+" : "+dialogue.getReply(i));
         }

         int playerReply = -1;

         while(playerReply <0)
         {
             try
             {
                System.out.print("> ");             
                playerReply = stdIn.nextInt();
             
                if(playerReply <= 0 || playerReply > dialogue.getNumberOfReply()) // check if player chose a possible answer
                {
                    System.out.println("Soll ich neue Antworten erfinden?");
                    playerReply = -1;
                }
               else
               {
                   activeDialogue = dialogue.getReply(playerReply -1).getNextDialogue();
               }
             }
             catch(InputMismatchException e)
             {
                System.out.println("\"Wie bitte?\"");       // "I'm sorry?" 
             }
         }
     }
  }
}


Hello,

sorry for the late reply, but to be honest the last days I tried to learn more and more about objects. Thank’s to Bonbon-Chan for your script-example - the more I progressed the more I understood about what you are actually doing :slight_smile:

I managed to finish a small game to a point where it is playable. Now I would love to include a save function but I don’t know how to write and read data from a file. Basically I just need to store all the player related variables in a file and load them… Could someone point me into the right direction (Tutorial)?

greetings

EDIT: Nevermind. I think java.Io is the way to go :slight_smile:

Yep, java.io. But you still have 36 000 way to do it : raw data, serialization, binary file, text file, xml file, … :wink:

An easy and simple way is to use binary file with : DataInputStream / DataOutputStream


  DataOutputStream dos = new DataOutputStream(new FileOutputStream("./player.sav"));

  dos.writeUTF(name); // Write a string
  dos.writeInt(level);   // Write a int
  dos.writeBoolean(isLocked); // write a boolean

  dos.flush();
  dos.close();


  DataInputStream dis = new DataInputStream(new FileInputStream("./player.sav"));

  name = dis.readUTF();
  level = dis.readInt();
  isLock = dis.readBoolean();

  dis.close();

Pretty easy no ? (I didn’t catch exception in my examples)

Thank’s again Bonbon-chan,

the DataInput- /OutputStream was really easy to use , took me about 5 mins to write the load and save :slight_smile:

I have a question regarding recursion.
If I understood it right, recursion happens when I call a method from within itself, or (as in my dialogue-script) call two methods from each other.
So I guess it’s also recursive if I make something like this right?

mainMenu() -> startGame() -> startFight() -> exit() -> mainMenu()

But how can I get back to the main menu, after the player exited the current game (by dying, or chosing to leave) without using recursion? I hope I don’t annoy people with my questions :-\

greetings

Oh no that’s wrong. Menu, start, fight etc should be implemented as state machine. Each state has its own “next state” to put it simple. Each state has its own bussiness without bothering others… ok maybe passing some values for use.

Recursion is used to solve problem that has same pattern with different parameter(s), where the parameter is given first and the result is used by next recursion as parameter and so on. For example, finding route in grid based map (A* logic)

function(function(function(function(function(function(function…(function(function(x))))))))) = a result after process x with N times function which is end on some value so the flow climbs up again.

By itselft, recursion is NOT a problem. It can be use for algorithm (like sorting, parsing tree, …) without make really complicated code. There is technics to “unroll” recursion but it will be a lit too mush to explain that ;).

When you invoke method within another method, you should ask yourself : is there case, where I will never get to the “return” or the end of the function ?

For example, if I continue to do fights without dying ?

Most of the time, you can avoid problem using a return value and a loop. For example :


while(!finish)
{
  int state = doDialog();

  if (state == DO_FIGHT)
  {  
    state = doFight();
  }

  if (state == DIE)
  { 
    die();  
    finish = true;
  }
}

It is what ReBirth call using a state machine (well this one is not nice but simple to understand).

Okay I think I understand.

I should do it like this:



	public static void game()
	{
		boolean play = true;
		
		while(play == true)
		{
			play = Menu.mainMenu();    
		}
	}

public static boolean mainMenu()
	{
		boolean continuePlaying = true;
		
		switch(menuOption(2))
		{
		case 1:
	 		startGame();			// Start a new game.
			break;
		case 2:								
			continuePlaying = false;    // Exit the game. (before I would've used exit() here)
			break;
		}
		
		return continuePlaying; 
	} 

Is this what you mean? :slight_smile:

Greetings

More or less, yes. With practise, you will do better.

I see that you are using a lot of “static” method. It is not in object oriented philosophically.
In a perfect world, the main code should be something like :


 public class Game
{
  public static void main(String args[])
  {
     Game game = new Game();
     game.start();
  }
}

With almost everything else none static (if it is not shared, there is no point to put it static).

You are right. I still use “static” way too often. My “player” and “enemies” are objects already - I am currently rewriting the whole thing using objects and working on “state machines”.

Your replies help a lot for my understanding :slight_smile:

EDIT: Removed picture.

To put it simple, use static for class that’s not allowed to have multiple instance since all components inside your package only want it alone and same. For example, a class that load all resources for the game.

[quote=“ReBirth,post:17,topic:37150”]
…is a terrible idea and shouldn’t be made static. What happens when you want to go multiplayer and have both a client and server running in the same vm for the host? Or having separate resource pools for out-game and in-game? Or preloading the resources for the next level while still using the current level’s resources?

You don’t need “only ever one”, you need “currently only one”. The correct design pattern for that is to just create one object.

Well that’s why I have used static for my “player” before (since there is only one player).

Here is the problem I ran into. I created a new object of the type “player” in my combat class (since this is were all the magic happens and the player-class is needed the most) but now when I try to change one of it’s variables, for example the player’s name outside of the combat class I somehow need to access that certain object. So is that the right time and place to make it static (the pointer not the whole player-class I mean)?


public class Combat
{
	static Player player = new Player();     // create a new object and store the reference to that object in player
                                                                //        make it static so that other classes can use it
      ...
      ...
      ...
}


public class Menu
{
      ...
      ...
        Combat.player.creation();      
      ...
      ...
      ...
}

public class Player
{
      ...
      public void creation()
      {
            ...
            ...
      }
      ...
}

I haven’t studied your code in detail, but what I’d suggest is to create a single Player object at a higher level (in your Game or similar class), then pass it in as an argument to the Combat and Menus classes as needed.