@DarkSinfulMage
As you've found, you can't really wait for input in-line as your pseudocode said. When you press a key, an event gets fired on the UI thread. If you use that event to run the combat loop, and then want to wait for input, the UI thread will be waiting for the UI thread to fire a new event. That doesn't work, so the program freezes. There are two solutions: Either run the combat loop on a different thread, or structure it in a way it doesn't block the UI thread. The former option introduces a gazillion new bugs, so don't.
That means structuring your code in a non-blocking way, like so:
So now, at some point doCombatStage1 gets called, starting the combat loop. It does some stuff, then it adds a button to the GUI. It tells the GUI what text should appear on the button, and what it should do when it is pressed. The button gets added, doCombatStage1 is done, so the UI thread can finish doing what it needs to do (handling events, painting components, etc.). That means the user will now see the button and be able to click it. Once the button is clicked, its handler, doCombatStage2, will execute. It has two branches, so two buttons are added. Repeat as often as you like, making sure you eventually end up back at doCombatStage1. Ignore the ActionEvent parameters, you probably won't use them.
As you've found, you can't really wait for input in-line as your pseudocode said. When you press a key, an event gets fired on the UI thread. If you use that event to run the combat loop, and then want to wait for input, the UI thread will be waiting for the UI thread to fire a new event. That doesn't work, so the program freezes. There are two solutions: Either run the combat loop on a different thread, or structure it in a way it doesn't block the UI thread. The former option introduces a gazillion new bugs, so don't.
That means structuring your code in a non-blocking way, like so:
Code:
// In Combat
public void doCombatStage1() {
// Do stuff
gui.addButton("Go To Stage 2", this::doCombatStage2);
}
public void doCombatStage2(ActionEvent evt) {
// Do more stuff
gui.addButton("Go To Stage 3, Option 1", this::doCombatStage3Opt1);
gui.addButton("Go To Stage 3, Option 2", this::doCombatStage3Opt2);
}
public void doCombatStage3Opt1(ActionEvent evt) {
// Yet more stuff
this.endOfTurn(); // calls doCombatStage1
}
// ^ Same for doCombatStage3Opt2
// In GUI
public void addButton(String text, ActionListener listener) {
// We don't want plain grey buttons...
// FancyColoredButton extends javax.swing.JButton
FancyColoredButton btn = new FancyColoredButton(text);
btn.addActionListener(listener);
this.buttonContainer.add(btn);
}
So now, at some point doCombatStage1 gets called, starting the combat loop. It does some stuff, then it adds a button to the GUI. It tells the GUI what text should appear on the button, and what it should do when it is pressed. The button gets added, doCombatStage1 is done, so the UI thread can finish doing what it needs to do (handling events, painting components, etc.). That means the user will now see the button and be able to click it. Once the button is clicked, its handler, doCombatStage2, will execute. It has two branches, so two buttons are added. Repeat as often as you like, making sure you eventually end up back at doCombatStage1. Ignore the ActionEvent parameters, you probably won't use them.