CraftedGaming CraftedGaming - 3 years ago 143
Java Question

Java text-adventure game swing components not updating correctly and enum gets selected twice per function run

I am intrigued by the bug that's appearing in the code that I've written. It works perfectly in C# but somehow it doesn't work in Java. I'm not sure if it has something to do with the second time I set it. I've checked it with my other class that passes the buttons into this class and I find nothing that would affect this class. Another thing to note is that all the Swing components aren't null when they're passed into this class.

My main problem is that all buttons do not update after setting the text for the second time.

public class StoryManager extends Text{
// this is the body of the story
private JTextPane textPane;
// the choices that the user can make
private JButton btnChoice1;
// this would determine which ending the user will get
private short ending = 0;

/**
* checkpoint for each part of the story
* */
public enum Part{
START, A_JOKE, B_BASE
}
// the part that the user is currently in
Part userPart;

/**
* This runs the story.
* */
public void PlayStory() {
printText();
processDecision();
}
/**
* This attaches the buttons and the textPane to their corresponding variables in the class.
* */
public StoryManager(WindowHandler windowHandler) {
// pane
textPane = windowHandler.getTextPane();
// buttons
btnChoice1 = windowHandler.getBtnChoice1();
// set the user to start the story from the beginning
userPart = Part.START;
}

@Override
public void printText() {
switch(userPart) {
case START:
System.out.println("Start event started");
textPane.setText("some text"
+ "[1] option 1\n");
break;
case A_JOKE:
System.out.println("A_JOKE event started");
textPane.setText("another text 1"
+ "[1] option 1\n");
break;
case B_BASE:
System.out.println("B_BASE event started");
textPane.setText("another text 2"
+ "[1] option 1\n");

break;
default:
}
textPane.setCaretPosition(0);
textPane.repaint();
}
/**
* Enables all the buttons.
* */
private void EnableAllButtons() {
if(!btnChoice1.isEnabled()) {
btnChoice1.setEnabled(true);
btnChoice1.setOpaque(true);
btnChoice1.setContentAreaFilled(true);
btnChoice1.setBorderPainted(true);
}
btnChoice1.repaint();
//System.out.println("Button1: "+btnChoice1.isEnabled());
}
/**
* Temporarily disables the other buttons and runs the PlayStory() again to continue the story.
*
* @param button The button that is clicked.
* */
private void SetDecisionText(JButton button) {
button.setText("Next");
btnChoice1.revalidate();
PlayStory();
}
/**
* Sets the action of the button per part.
* */
@Override
public void processDecision() {
switch(userPart) {
case START:
btnChoice1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
ending -= 10;
userPart = Part.A_JOKE;
SetDecisionText(btnChoice1);
}
});
break;
case A_JOKE:
btnChoice1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
btnChoice1.setText("Choice 1");
//btnChoice1.revalidate(); <- tried this but it isn't working
GoToBase();
}
});
break;
case B_BASE:
System.out.println("Updated with B_BASE");
btnChoice1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
ending -= 10;
userPart = Part.B_ARGUE;
SetDecisionText(btnChoice1);
}
});
}

}
private void GoToBase() {
EnableAllButtons();
switch(userPart) {
case A_JOKE:
userPart = Part.B_BASE;
System.out.println("GotoBase: " + userPart);
break;
default:
System.out.println("Error");
}
PlayStory();
}

}


I'll share the output of my program here. I selected the first button twice then this happens. When I press it again,
userPart
becomes null or something.

// pressed play
Start event started

// click 1st button
A_JOKE event started

// click 1st button again
GotoBase: B_BASE
B_BASE event started
Updated with B_BASE
A_JOKE event started

// click 1st button for the third time
GotoBase: B_BASE
B_BASE event started
Updated with B_BASE
Error
A_JOKE event started

Answer Source

You appear to be adding ActionListeners to your buttons within the logical portion of your code, and your doing this means that buttons will have ActionListeners added to them multiple times, whenever the processDecision() method is called, resulting eventually in buttons with multiple listeners added to them which is not what you want. The listeners should be added once, at component creation.

Or if you are going to want to swap ActionListeners, then be sure to remove the old listeners when replacing with the new.

There are likely other logical problems in your code, but this is the limit of what I can suggest until you are able to provide a valid Minimal, Complete, and Verifiable Example Program for us. This is where you condense your code into the smallest bit that still compiles and runs, has no outside dependencies (such as need to link to a database or images), has no extra code that's not relevant to your problem, but still demonstrates your problem.

A large problem that I see is that your code has very high coupling and low cohesion, making for very brittle code, and code that is hard to debug. Consider refactoring, separating your model (the logic portion of your program) from your view (the GUI portion) and separating the data from the code, getting rid of the hard-coded display text for instance, and putting it into a data repository, be it a text file, database or whatever works best.

example of removing listeners:

case A_JOKE:
    for (ActionListener l : btnChoice1.getActionListeners()) {
        btnChoice1.removeActionListener(l);
    }
    btnChoice1.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            btnChoice1.setText("Choice 1");
            // btnChoice1.revalidate(); <- tried this but it isn't
            // working
            GoToBase();
        }
    });
    break;

As an aside, you will want to learn and use Java naming conventions which are different than those used for C#. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download