Commit ab729db4 authored by Carl Johan Gützkow's avatar Carl Johan Gützkow 🎮
Browse files

enhance(dialogs): diolog builder

parent e0f8a9c1
......@@ -11,7 +11,7 @@ import java.io.*;
* armies to and from a file.
*
* @author Carl Gützkow
* @version 1.2 08.04.2022
* @version 1.3 13.04.2022
*/
public class ArmyFileHandler {
......@@ -19,7 +19,7 @@ public class ArmyFileHandler {
/**
* Gets the amount of units
* that were scipped when reading from file.
* that were skipped when reading from file.
*
* @return readLinesSkipped - amount of lines skipped when
* reading army from file because of invalid unit.
......@@ -81,8 +81,8 @@ public class ArmyFileHandler {
while ((line = bufferedReader.readLine()) != null) {
try {
String[] unit = line.split(",");
String unitType = unit[0];
String name = unit[1];
String unitType = unit[0].trim();
String name = unit[1].trim();
int health = Integer.parseInt(unit[2].trim());
army.addUnit(unitFactory.createUnit(unitType, name, health));
......
......@@ -4,24 +4,30 @@ import edu.ntnu.idatt2001.carljgu.Terrain;
import edu.ntnu.idatt2001.carljgu.battle.Battle;
import edu.ntnu.idatt2001.carljgu.FileExtensionException;
import edu.ntnu.idatt2001.carljgu.ArmyFileHandler;
import edu.ntnu.idatt2001.carljgu.client.dialogs.DialogBoxBuilder;
import edu.ntnu.idatt2001.carljgu.units.Army;
import edu.ntnu.idatt2001.carljgu.units.Unit;
import edu.ntnu.idatt2001.carljgu.units.UnitFactory;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseButton;
import javafx.scene.text.Text;
import static javafx.scene.control.Alert.AlertType.*;
/**
* A controller class which
* handles events in the fxml file.
*
* @version 1.4 08.04.2022
* @version 1.5 13.04.2022
* @author Carl Gützkow
*/
public class BattleController implements Initializable {
......@@ -59,22 +65,36 @@ public class BattleController implements Initializable {
* display its information in the
* given text elements.
*
* @param army - int - the selected army to import.
* @param armyNumber - int - the selected army to import.
* 0 for army one and 1 for army two
*/
private void importArmy(int army) {
private void importArmy(int armyNumber) {
ArmyFileHandler fileHandler = new ArmyFileHandler();
new DialogBoxBuilder(ERROR).build().showAndWait();
try {
filePaths[army] = fileHandler.getFilePath();
filePaths[armyNumber] = fileHandler.getFilePath();
if (filePaths[army] == null)
if (filePaths[armyNumber] == null)
return;
armies[army] = fileHandler.readArmyFromFile(filePaths[army]);
displayArmy(army, armies[army]);
} catch (IOException | FileExtensionException e) {
System.out.println("An error occurred");
armies[armyNumber] = fileHandler.readArmyFromFile(filePaths[armyNumber]);
displayArmy(armyNumber, armies[armyNumber]);
new DialogBoxBuilder(INFORMATION)
.addTitle("Army imported")
.addMessage("Army was successfully imported.\n" +
"In the process " + fileHandler.getReadLinesSkipped() + " units were corrupted")
.build().showAndWait();
} catch (FileExtensionException e) {
new DialogBoxBuilder(ERROR)
.addTitle("File is not supported")
.addMessage(e.getMessage())
.build().showAndWait();
} catch (IOException e) {
new DialogBoxBuilder(ERROR)
.addHeader("File could not be loaded")
.addMessage(DialogBoxBuilder.recurringErrorMessage)
.build().showAndWait();
}
scores[0] = 0;
scores[1] = 0;
......@@ -105,20 +125,17 @@ public class BattleController implements Initializable {
* Run when clicking on reset armies.
* Deep copies the armies and creates a new battle.
* Also resets the labels and texts.
*
* @return result - boolean - true if the reset was a success, false otherwise
*/
@FXML
void resetArmies() {
boolean resetArmies() {
UnitFactory factory = new UnitFactory();
boolean result = false;
try {
ArrayList<Unit> unitsOne =
armies[0].getAllUnits()
.stream()
.map(Unit::deepCopyUnit)
.collect(Collectors.toCollection(ArrayList::new));
ArrayList<Unit> unitsTwo =
armies[1].getAllUnits()
.stream()
.map(Unit::deepCopyUnit)
.collect(Collectors.toCollection(ArrayList::new));
ArrayList<Unit> unitsOne = factory.deepCopyBasicUnits(armies[0].getAllUnits());
ArrayList<Unit> unitsTwo = factory.deepCopyBasicUnits(armies[1].getAllUnits());
armyOne = new Army(armies[0].getName(), unitsOne);
armyTwo = new Army(armies[1].getName(), unitsTwo);
......@@ -128,9 +145,19 @@ public class BattleController implements Initializable {
displayArmy(0, armyOne);
displayArmy(1, armyTwo);
} catch (IllegalArgumentException | NullPointerException e) {
System.out.println(e.getMessage());
result = true;
} catch (IllegalArgumentException e) {
new DialogBoxBuilder(ERROR)
.addMessage(e.getMessage())
.build().showAndWait();
} catch (NullPointerException e) {
new DialogBoxBuilder(NONE)
.addTitle("Army does not exist")
.addMessage("Armies have not been imported")
.build().showAndWait();
}
return result;
}
/**
......@@ -141,8 +168,10 @@ public class BattleController implements Initializable {
*/
@FXML
void runSimulation() {
if (!resetArmies())
return;
try {
resetArmies();
Army winningArmy = battle.simulate();
attackList.getItems().addAll(battle.getAttackLog());
......@@ -155,22 +184,26 @@ public class BattleController implements Initializable {
displayArmy(0, armyOne);
displayArmy(1, armyTwo);
} catch (UnsupportedOperationException | NullPointerException e) {
System.out.println(e.getMessage());
new DialogBoxBuilder(ERROR)
.addMessage(e.getMessage())
.build().showAndWait();
}
}
/**
* Updates the information about an army
* in the chosen elements..
* in the chosen elements.
* Uses both armyNumber and army to be able to display armies that are not
* the have been changed. For example after a battle.
*
* @param armySide int - 0 or 1 depending on which elements to use to display an army.
* @param armyNumber int - 0 or 1 depending on which elements to use to display an army.
* @param army Army - the army to display.
* @throws NullPointerException thrown if the army is null
*/
public void displayArmy(int armySide, Army army) throws NullPointerException {
public void displayArmy(int armyNumber, Army army) throws NullPointerException {
if (army == null) throw new NullPointerException("Army is a a null object");
armyNames[armySide].setText(army.getName());
armyNames[armyNumber].setText(army.getName());
String armyRepresentation =
"Infantry units: " + army.getInfantryUnits().size() + "\n" +
......@@ -178,17 +211,44 @@ public class BattleController implements Initializable {
"Cavalry units: " + army.getCavalryUnits().size() + "\n" +
"Commander units: " + army.getCommanderUnits().size() + "\n";
armySummaries[armySide].setText(armyRepresentation);
armySummaries[armyNumber].setText(armyRepresentation);
armyImportPaths[armySide].setText(filePaths[armySide]);
armyImportPaths[armyNumber].setText(filePaths[armyNumber]);
armyUnitListViews.get(armySide).getItems().clear();
armyUnitListViews.get(armySide).getItems().addAll(army.getAllUnits());
armyUnitListViews.get(armyNumber).getItems().clear();
armyUnitListViews.get(armyNumber).getItems().addAll(army.getAllUnits());
}
/**
* Run when the fxml file is first.
* Fills in the tables of the armies.
* Sets a selection model that is activated when
* the list view receives a double click.
*
* @param armyNumber int - the number of the list view
* which is to be updated and corresponds to the army shown.
*/
public void setDoubleClickSelectionModeOnListView(int armyNumber) {
armyUnitListViews.get(armyNumber).setOnMouseClicked(click -> {
if (click.getButton() == MouseButton.PRIMARY && click.getClickCount() == 2) {
Unit selectedUnit = armyUnitListViews.get(armyNumber).getSelectionModel().getSelectedItem();
if (selectedUnit != null) {
Optional<ButtonType> optional = new DialogBoxBuilder(CONFIRMATION)
.addTitle("Delete unit?")
.addMessage("Are you sure you want to delete this unit?")
.build().showAndWait();
boolean result = optional.filter(buttonType -> buttonType == ButtonType.OK).isPresent();
if (result) {
armies[armyNumber].remove(selectedUnit);
displayArmy(armyNumber, armies[armyNumber]);
}
}
}
});
}
/**
* Run when the fxml file is first loaded.
* Fills in the tables of information for the armies
* and sets a double click selection model.
*
* @param url - URL - a Uniform Resource Loader
* @param resourceBundle - ResourceBundle
......@@ -205,5 +265,9 @@ public class BattleController implements Initializable {
armyUnitListViews.add(armyTwoUnitsListView);
armies[0] = armyOne;
armies[1] = armyTwo;
setDoubleClickSelectionModeOnListView(0);
setDoubleClickSelectionModeOnListView(1);
}
}
package edu.ntnu.idatt2001.carljgu.client.dialogs;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
/**
* A dialog box that extends alert to use
* a builder to build a dialog box.
*
* @author Carl Gützkow
* @version 1.1 17.04.2022
*/
public class DialogBox extends Alert {
/**
* Constructor for a dialog box
* that uses a builder to set
* attributes. If the alert type is NONE,
* the window will only close if there is at
* least one button.
* If the builder has no image defined, then
* the default image is used. Otherwise, this
* constructor will add image from resource folder.
*
* @param builder
*/
public DialogBox(DialogBoxBuilder builder) {
super(builder.getAlertType());
this.setTitle(builder.getTitle());
this.setHeaderText(builder.getHeader());
this.setContentText(builder.getMessage());
if (builder.getAlertType() == AlertType.NONE) {
this.getDialogPane().getButtonTypes().add(ButtonType.OK);
}
if (builder.getImage() != null) {
this.setGraphic(new ImageView(builder.getImage()));
Stage stage = (Stage) this.getDialogPane().getScene().getWindow();
stage.getIcons().add(builder.getImage());
}
}
}
package edu.ntnu.idatt2001.carljgu.client.dialogs;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
/**
* A builder class which simplifies the
* DialogBox class. Using a builder makes it easy
* to create complex dialogs while still avoiding
* the telescoping constructors problem.
* It also makes it easier to add attributes in the future.
*
* @author Carl Gützkow
* @version 1.1 17.04.2022
*/
public class DialogBoxBuilder {
public static final String recurringErrorMessage =
"If this a recurring and unsolvable event, please contact the creator of this program.";
private AlertType alertType;
private String title;
private String header;
private String message;
private Image image;
/**
* Instantiates a new dialog box builder.
* Sets the title and message to a default.
* They are still optional to set.
*
* @param alertType AlertType - enum from inside the Alert class.
* Either ERROR, INFORMATION, NONE, CONFIRMATION or WARNING
*/
public DialogBoxBuilder(AlertType alertType) {
this.alertType = alertType;
this.title = alertType.name();
this.message = recurringErrorMessage;
}
/**
* Create the dialog box with the
* current configurations from this
* builder object.
*
* @return dialogBox - DialogBox - a dialog box with this builder's configurations.
*/
public DialogBox build() {
return new DialogBox(this);
}
/**
* Add title to the dialog box builder configuration.
* The title is displayed on the top of the window.
*
* @param title String - the title of the dialog box
* @return dialogBoxBuilder - DialogBoxBuilder - returned to add further configurations.
*/
public DialogBoxBuilder addTitle(String title) {
this.title = title;
return this;
}
/**
* Add header to the dialog box builder configuration.
* A header is displayed above the message
*
* @param header String - the header of the dialog box
* @return dialogBoxBuilder - DialogBoxBuilder - returned to add further configurations.
*/
public DialogBoxBuilder addHeader(String header) {
this.header = header;
return this;
}
/**
* Add message to the dialog box builder configuration.
*
* @param message String - the message of the dialog box
* @return dialogBoxBuilder - DialogBoxBuilder - returned to add further configurations.
*/
public DialogBoxBuilder addMessage(String message) {
this.message = message;
return this;
}
/**
* Add image to the dialog box builder configuration.
* The image is displayed as both the window icon
* and on the actual scene.
*
* @param imageName String - the name of the image to display.
* @return dialogBoxBuilder - DialogBoxBuilder - returned to add further configurations.
*/
public DialogBoxBuilder addImage(String imageName) {
this.image = new Image(imageName);
return this;
}
/**
* Gets the alert type for the dialog box.
*
* @return alertType - AlertType - enum from inside the Alert class.
* Either ERROR, INFORMATION, NONE, CONFIRMATION or WARNING
*/
public AlertType getAlertType() {
return alertType;
}
/**
* Gets the title of the dialog box.
*
* @return title - String - the dialog box' title
*/
public String getTitle() {
return title;
}
/**
* Gets the header for the dialog box.
*
* @return header - String - the dialog box' header
*/
public String getHeader() {
return header;
}
/**
* Gets the message for the dialog box.
*
* @return message - String - the dialog box' message
*/
public String getMessage() {
return message;
}
/**
* Gets the image for the dialog box-
*
* @return image - Image - the dialog box' icon and image
*/
public Image getImage() {
return image;
}
}
......@@ -15,7 +15,7 @@ import java.util.ArrayList;
* the exception is handled in the factory.
*
* @author Carl Gützkow
* @version 1.2 08.04.2022
* @version 1.3 13.04.2022
*/
public class UnitFactory {
......@@ -26,15 +26,14 @@ public class UnitFactory {
* If the unit type could not be
* found, it will return null instead.
*
* @param untrimmedUnitType String - the type of unit to create.
* @param unitType String - the type of unit to create.
* @param name String - name of the created unit.
* @param health int - amount of health the unit starts width
* @return unit - Unit - an instance of specialized unit with name and health specified
* @throws IllegalArgumentException thrown if the unit could not be created or
* if the type of unit does not exist.
*/
public Unit createUnit(String untrimmedUnitType, String name, int health) throws IllegalArgumentException {
String unitType = untrimmedUnitType.trim();
public Unit createUnit(String unitType, String name, int health) throws IllegalArgumentException {
if (unitType.equals("InfantryUnit"))
return new InfantryUnit(name, health);
if (unitType.equals("RangedUnit"))
......@@ -69,4 +68,20 @@ public class UnitFactory {
}
return units;
}
/**
* Deep copies units, but ignores overridden
* attack and armor points.
*
* @param units ArrayList - a list of units.
* @return new_units - ArrayList - a deep copied list of all units
* @throws IllegalArgumentException thrown if any units could not be
* created or if unit type does not exist.
*/
public ArrayList<Unit> deepCopyBasicUnits(ArrayList<Unit> units) throws IllegalArgumentException {
ArrayList<Unit> new_units = new ArrayList<>();
for (Unit unit : units)
new_units.add(createUnit(unit.getClass().getSimpleName(), unit.getName(), unit.getHealth()));
return new_units;
}
}
......@@ -8,4 +8,6 @@ module edu.ntnu.idatt2001.carljgu.client {
exports edu.ntnu.idatt2001.carljgu.units ;
exports edu.ntnu.idatt2001.carljgu ;
exports edu.ntnu.idatt2001.carljgu.battle;
exports edu.ntnu.idatt2001.carljgu.client.dialogs;
opens edu.ntnu.idatt2001.carljgu.client.dialogs to javafx.fxml;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment