Merge branch 'NewUI_merge' into story1266_3d_model_factory

# Conflicts:
#	src/main/java/seng302/visualiser/controllers/RaceViewController.java
This commit is contained in:
Calum
2017-09-12 17:09:47 +12:00
207 changed files with 4809 additions and 23093 deletions
@@ -7,9 +7,10 @@ import javafx.scene.control.TextField;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.utilities.Sounds;
import seng302.visualiser.ClientToServerThread;
public class CustomizationController {
public class CustomizationController_old {
@FXML
private TextField nameField;
@@ -20,7 +21,7 @@ public class CustomizationController {
@FXML
private Button customizeSubmit;
private LobbyController lc;
private LobbyController_old lc;
private ClientToServerThread socketThread;
private Stage windowStage;
@@ -34,7 +35,8 @@ public class CustomizationController {
@FXML
public void submitCustomization() {
System.out.println("Attempting to send");
Sounds.playButtonClick();
// System.out.println("Attempting to send");
socketThread.sendCustomizationRequest(CustomizeRequestType.NAME, nameField.getText().getBytes());
// TODO: 16/08/17 ajm412: Turn colors into byte array.
Color color = boatColorPicker.getValue();
@@ -54,7 +56,7 @@ public class CustomizationController {
windowStage.close();
}
public void setLobbyController(LobbyController lc) {
public void setLobbyController(LobbyController_old lc) {
this.lc = lc;
}
@@ -15,10 +15,12 @@ import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import seng302.model.ClientYacht;
import seng302.utilities.Sounds;
public class FinishScreenViewController implements Initializable {
@@ -85,6 +87,12 @@ public class FinishScreenViewController implements Initializable {
}
public void switchToStartScreenView() {
Sounds.playButtonClick();
//TODO merge fix
setContentPane("/views/StartScreenView.fxml");
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}
@@ -1,248 +1,227 @@
package seng302.visualiser.controllers;
import com.sun.media.jfxmedia.logging.Logger;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState;
import seng302.model.ClientYacht;
import seng302.model.Colors;
import seng302.model.Limit;
import seng302.model.RaceState;
import seng302.visualiser.ClientToServerThread;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.utilities.Sounds;
import seng302.visualiser.GameView;
import seng302.visualiser.controllers.cells.PlayerCell;
/**
* A class describing the actions of the lobby screen
* Created by wmu16 on 10/07/17.
*/
public class LobbyController {
public class LobbyController implements Initializable {
public enum CloseStatus {
LEAVE,
READY
}
//--------FXML BEGIN--------//
@FXML
private VBox playerListVBox;
@FXML
private ScrollPane playerListScrollPane;
@FXML
private JFXButton customizeButton, leaveLobbyButton, beginRaceButton;
@FXML
private StackPane serverListMainStackPane;
@FXML
private Label serverName;
@FXML
private Label mapName;
@FXML
private Pane serverMap;
//---------FXML END---------//
@FunctionalInterface
public interface LobbyCloseListener {
void notify(CloseStatus exitCause);
}
@FXML
private Text lobbyIpText;
@FXML
private Button readyButton;
@FXML
private Button customizeButton;
@FXML
private TextArea playerOneTxt;
@FXML
private TextArea playerTwoTxt;
@FXML
private TextArea playerThreeTxt;
@FXML
private TextArea playerFourTxt;
@FXML
private TextArea playerFiveTxt;
@FXML
private TextArea playerSixTxt;
@FXML
private TextArea playerSevenTxt;
@FXML
private TextArea playerEightTxt;
@FXML
private ImageView firstImageView;
@FXML
private ImageView secondImageView;
@FXML
private ImageView thirdImageView;
@FXML
private ImageView fourthImageView;
@FXML
private ImageView fifthImageView;
@FXML
private ImageView sixthImageView;
@FXML
private ImageView seventhImageView;
@FXML
private ImageView eighthImageView;
@FXML
private Text timeUntilStart;
@FXML
private Text courseNameText;
private List<ImageView> imageViews = new ArrayList<>();
private List<TextArea> listViews = new ArrayList<>();
private List<LobbyController_old.LobbyCloseListener> lobbyListeners = new ArrayList<>();
private RaceState raceState;
private JFXDialog customizationDialog;
public Color playersColor;
private Map<Integer, ClientYacht> playerBoats;
private Double mapWidth, mapHeight;
private GameView gameView;
private ClientToServerThread socketThread;
@Override
public void initialize(URL location, ResourceBundle resources) {
private Stage customizeStage;
this.playerBoats = ViewManager.getInstance().getGameClient().getAllBoatsMap();
private Color playersColor;
if (this.playersColor == null) {
this.playersColor = Colors.getColor(ViewManager.getInstance().getGameClient().getServerThread().getClientId() - 1);
}
private int MAX_NUM_PLAYERS = 8;
private Integer playerID;
leaveLobbyButton.setOnMouseReleased(event -> leaveLobby());
beginRaceButton.setOnMouseReleased(event -> beginRace());
leaveLobbyButton.setOnMouseReleased(event -> {
Sounds.playButtonClick();
leaveLobby();
});
private List<LobbyCloseListener> lobbyListeners = new ArrayList<>();
private ObservableList<String> players;
beginRaceButton.setOnMouseReleased(event -> {
Sounds.playButtonClick();
beginRace();
});
/**
* Add all FXObjects to lists and initialize images.
*/
public void initialize() {
Collections.addAll(listViews,
playerOneTxt, playerTwoTxt, playerThreeTxt, playerFourTxt, playerFiveTxt, playerSixTxt,
playerSevenTxt, playerEightTxt
);
Collections.addAll(imageViews,
firstImageView, secondImageView, thirdImageView, fourthImageView,
fifthImageView, sixthImageView, seventhImageView, eighthImageView
);
initialiseImageView();
Platform.runLater(() -> {
serverName.setText(ViewManager.getInstance().getProperty("serverName"));
mapName.setText(ViewManager.getInstance().getProperty("mapName"));
ViewManager.getInstance().getPlayerList().addListener((ListChangeListener<String>) c -> Platform.runLater(this::refreshPlayerList));
ViewManager.getInstance().getPlayerList().setAll(ViewManager.getInstance().getPlayerList().sorted());
});
Platform.runLater(() -> {
Integer playerId = ViewManager.getInstance().getGameClient().getServerThread().getClientId();
String name = ViewManager.getInstance().getGameClient().getPlayerNames().get(playerId - 1);
Color playerColor = Colors.getColor( playerId - 1);
customizationDialog = ViewManager.getInstance().loadCustomizationDialog(serverListMainStackPane, this, playerColor, name);
customizeButton.setOnMouseReleased(event -> {
Sounds.playButtonClick();
customizationDialog.show();
});
});
leaveLobbyButton.setOnMouseEntered(e -> Sounds.playHoverSound());
customizeButton.setOnMouseEntered(e -> Sounds.playHoverSound());
beginRaceButton.setOnMouseEntered(e -> Sounds.playHoverSound());
initMapPreview();
}
private void refreshMapView(){
RaceXMLData raceData = ViewManager.getInstance().getGameClient().getCourseData();
List<Limit> border = raceData.getCourseLimit();
List<CompoundMark> marks = new ArrayList<CompoundMark>(raceData.getCompoundMarks().values());
List<Corner> corners = raceData.getMarkSequence();
gameView.setSize(mapWidth, mapHeight);
// Update game view
gameView.updateBorder(border);
gameView.updateCourse(marks, corners);
}
private void getPlayerColors() {
timeUntilStart.setText("Waiting For Host...");
}
/**
* Updates player names.
*
*/
private void updatePlayers() {
//Update players if one added.
for (int i = 0; i < players.size(); i++) {
listViews.get(i).setText(players.get(i));
if (playerID == (i + 1)) {
listViews.get(i).setText(listViews.get(i).getText() + " (YOU)");
}
imageViews.get(i).setVisible(true);
}
//Update empty text fields if player left.
for (int i = MAX_NUM_PLAYERS-1; i >= players.size(); i--) {
listViews.get(i).setText("");
imageViews.get(i).setVisible(false);
}
private void initMapPreview() {
gameView = new GameView();
gameView.setHorizontalBuffer(330d);
mapWidth = 770d;
mapHeight = 574d;
// Add game view
serverMap.getChildren().clear();
serverMap.getChildren().add(gameView);
serverMap.widthProperty().addListener((observable, oldValue, newValue) -> {
mapWidth = newValue.doubleValue();
refreshMapView();
});
serverMap.heightProperty().addListener((observable, oldValue, newValue) -> {
mapHeight = newValue.doubleValue();
refreshMapView();
});
}
/**
* Sets all images and hides them till players join.
*
*/
private void initialiseImageView() {
for (ImageView viewer : imageViews) {
viewer.setImage(
new Image(
RaceViewController.class.getResourceAsStream(
"/pics/sail.png")
)
);
viewer.setVisible(false);
}
}
@FXML
public void customize() {
Parent root;
try {
FXMLLoader fxmlLoader = new FXMLLoader(LobbyController.class.getResource("/views/customizeView.fxml"));
root = fxmlLoader.load();
root.getStylesheets().add("/css/master.css");
customizeStage = new Stage();
customizeStage.setTitle("Customize Boat");
customizeStage.setScene(new Scene(root, 700, 450));
CustomizationController cc = fxmlLoader.getController();
cc.setServerThread(this.socketThread);
cc.setPlayerName(this.players.get(playerID - 1));
if (this.playersColor == null) {
this.playersColor = Colors.getColor(playerID - 1);
}
cc.setPlayerColor(this.playersColor);
cc.setStage(customizeStage); // pass the stage through so it can be closed later.
cc.setLobbyController(this);
customizeStage.show();
} catch (IOException e) {
Logger.logMsg(4, "Failed to load Customization View from resources.");
}
}
public void setSocketThread(ClientToServerThread thread) {
this.socketThread = thread;
}
@FXML
public void leaveLobbyButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function!
GameState.setCurrentStage(GameStages.CANCELLED);
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game
for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.LEAVE);
}
@FXML
public void readyButtonPressed() {
GameState.setCurrentStage(GameStages.PRE_RACE);
// Do countdown logic here
for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.READY);
private void beginRace() {
beginRaceButton.setDisable(true);
customizeButton.setDisable(true);
GameState.setCurrentStage(GameStages.PRE_RACE);
GameState.resetStartTime();
Platform.runLater(()-> ViewManager.getInstance().getGameClient().startGame());
}
public void setTitle (String title) {
lobbyIpText.setText(title);
/**
*
*/
private void refreshPlayerList() {
playerListVBox.getChildren().clear();
if (this.playerBoats == null || this.playerBoats.size() == 0) {
this.playerBoats = ViewManager.getInstance().getGameClient().getAllBoatsMap();
}
// TODO: 12/09/2017 ajm412: Make it so that it only removes players who's details have changed.
for (Integer playerId : playerBoats.keySet()) {
VBox pane = null;
ClientYacht yacht = playerBoats.get(playerId);
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/views/cells/PlayerCell.fxml"));
loader.setController(new PlayerCell(playerId, yacht.getBoatName(), yacht.getColour()));
try {
pane = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
playerListVBox.getChildren().add(pane);
}
}
public void setCourseName(String courseName){
courseNameText.setText(courseName);
/**
*
*/
private void leaveLobby() {
ViewManager.getInstance().getGameClient().stopGame();
ViewManager.getInstance().goToStartView();
}
public void addCloseListener(LobbyCloseListener listener) {
lobbyListeners.add(listener);
}
public void setPlayerListSource (ObservableList<String> players) {
this.players = players;
players.addListener((ListChangeListener<? super String>) (lcl) ->
Platform.runLater(this::updatePlayers)
);
Platform.runLater(this::updatePlayers);
}
public void setPlayerID(Integer id) {
playerID = id;
/**
*
*/
public void disableReadyButton() {
this.beginRaceButton.setDisable(true);
this.beginRaceButton.setText("Waiting for host...");
}
/**
*
* @param raceState
*/
public void updateRaceState(RaceState raceState){
this.raceState = raceState;
/*if (this.customizeStage != null) {
this.customizeStage.close();
}*/ // TODO: 17/08/17 ajm412: close the customization window if the host starts the game while customizing
if (!customizeButton.isDisabled()) {
customizeButton.setDisable(true);
}
timeUntilStart.setText("Starting in: " + raceState.getRaceTimeStr());
this.beginRaceButton.setText("Starting in: " + raceState.getRaceTimeStr());
}
public void disableReadyButton () {
readyButton.setDisable(true);
readyButton.setVisible(false);
public void setBoats(Map<Integer, ClientYacht> boats) {
this.playerBoats = boats;
}
public void setPlayersColor(Color playerColor) {
this.playersColor = playerColor;
public void closeCustomizationDialog() {
customizationDialog.close();
}
}
@@ -0,0 +1,249 @@
package seng302.visualiser.controllers;
import com.sun.media.jfxmedia.logging.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState;
import seng302.model.Colors;
import seng302.model.RaceState;
import seng302.visualiser.ClientToServerThread;
/**
* A class describing the actions of the lobby screen
* Created by wmu16 on 10/07/17.
*/
public class LobbyController_old {
public enum CloseStatus {
LEAVE,
READY
}
@FunctionalInterface
public interface LobbyCloseListener {
void notify(CloseStatus exitCause);
}
@FXML
private Text lobbyIpText;
@FXML
private Button readyButton;
@FXML
private Button customizeButton;
@FXML
private TextArea playerOneTxt;
@FXML
private TextArea playerTwoTxt;
@FXML
private TextArea playerThreeTxt;
@FXML
private TextArea playerFourTxt;
@FXML
private TextArea playerFiveTxt;
@FXML
private TextArea playerSixTxt;
@FXML
private TextArea playerSevenTxt;
@FXML
private TextArea playerEightTxt;
@FXML
private ImageView firstImageView;
@FXML
private ImageView secondImageView;
@FXML
private ImageView thirdImageView;
@FXML
private ImageView fourthImageView;
@FXML
private ImageView fifthImageView;
@FXML
private ImageView sixthImageView;
@FXML
private ImageView seventhImageView;
@FXML
private ImageView eighthImageView;
@FXML
private Text timeUntilStart;
@FXML
private Text courseNameText;
private List<ImageView> imageViews = new ArrayList<>();
private List<TextArea> listViews = new ArrayList<>();
private RaceState raceState;
private ClientToServerThread socketThread;
private Stage customizeStage;
private Color playersColor;
private int MAX_NUM_PLAYERS = 8;
private Integer playerID;
private List<LobbyCloseListener> lobbyListeners = new ArrayList<>();
private ObservableList<String> players;
/**
* Add all FXObjects to lists and initialize images.
*/
public void initialize() {
Collections.addAll(listViews,
playerOneTxt, playerTwoTxt, playerThreeTxt, playerFourTxt, playerFiveTxt, playerSixTxt,
playerSevenTxt, playerEightTxt
);
Collections.addAll(imageViews,
firstImageView, secondImageView, thirdImageView, fourthImageView,
fifthImageView, sixthImageView, seventhImageView, eighthImageView
);
initialiseImageView();
timeUntilStart.setText("Waiting For Host...");
}
/**
* Updates player names.
*/
private void updatePlayers() {
//Update players if one added.
for (int i = 0; i < players.size(); i++) {
listViews.get(i).setText(players.get(i));
if (playerID == (i + 1)) {
listViews.get(i).setText(listViews.get(i).getText() + " (YOU)");
}
imageViews.get(i).setVisible(true);
}
//Update empty text fields if player left.
for (int i = MAX_NUM_PLAYERS-1; i >= players.size(); i--) {
listViews.get(i).setText("");
imageViews.get(i).setVisible(false);
}
}
/**
* Sets all images and hides them till players join.
*/
private void initialiseImageView() {
for (ImageView viewer : imageViews) {
viewer.setImage(
new Image(
RaceViewController.class.getResourceAsStream(
"/pics/sail.png")
)
);
viewer.setVisible(false);
}
}
@FXML
public void customize() {
Parent root;
try {
FXMLLoader fxmlLoader = new FXMLLoader(LobbyController_old.class.getResource("/views/customizeView.fxml"));
root = fxmlLoader.load();
root.getStylesheets().add("/css/master.css");
customizeStage = new Stage();
customizeStage.setTitle("Customize Boat");
customizeStage.setScene(new Scene(root, 700, 450));
CustomizationController_old cc = fxmlLoader.getController();
cc.setServerThread(this.socketThread);
cc.setPlayerName(this.players.get(playerID - 1));
if (this.playersColor == null) {
this.playersColor = Colors.getColor(playerID - 1);
}
cc.setPlayerColor(this.playersColor);
cc.setStage(customizeStage); // pass the stage through so it can be closed later.
cc.setLobbyController(this);
customizeStage.show();
} catch (IOException e) {
Logger.logMsg(4, "Failed to load Customization View from resources.");
}
}
public void setSocketThread(ClientToServerThread thread) {
this.socketThread = thread;
}
@FXML
public void leaveLobbyButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function!
GameState.setCurrentStage(GameStages.CANCELLED);
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game
for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.LEAVE);
}
@FXML
public void readyButtonPressed() {
GameState.setCurrentStage(GameStages.PRE_RACE);
// Do countdown logic here
for (LobbyCloseListener readyListener : lobbyListeners)
readyListener.notify(CloseStatus.READY);
customizeButton.setDisable(true);
}
public void setTitle (String title) {
lobbyIpText.setText(title);
}
public void setCourseName(String courseName){
courseNameText.setText(courseName);
}
public void addCloseListener(LobbyCloseListener listener) {
lobbyListeners.add(listener);
}
public void setPlayerListSource (ObservableList<String> players) {
this.players = players;
players.addListener((ListChangeListener<? super String>) (lcl) ->
Platform.runLater(this::updatePlayers)
);
Platform.runLater(this::updatePlayers);
}
public void setPlayerID(Integer id) {
playerID = id;
}
public void updateRaceState(RaceState raceState){
this.raceState = raceState;
/*if (this.customizeStage != null) {
this.customizeStage.close();
}*/ // TODO: 17/08/17 ajm412: close the customization window if the host starts the game while customizing
if (!customizeButton.isDisabled()) {
customizeButton.setDisable(true);
}
timeUntilStart.setText("Starting in: " + raceState.getRaceTimeStr());
}
public void disableReadyButton () {
readyButton.setDisable(true);
readyButton.setVisible(false);
}
//TODO here newui
public void setPlayersColor(Color playerColor) {
this.playersColor = playerColor;
}
}
@@ -1,5 +1,6 @@
package seng302.visualiser.controllers;
import com.jfoenix.controls.JFXButton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -9,6 +10,7 @@ import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@@ -16,6 +18,7 @@ import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.SubScene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
@@ -24,9 +27,14 @@ import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
@@ -35,17 +43,17 @@ import javafx.scene.shape.Polyline;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.StringConverter;
import seng302.gameServer.messages.BoatStatus;
import seng302.model.ClientYacht;
import seng302.model.RaceState;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.utilities.Sounds;
import seng302.visualiser.GameView3D;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
import seng302.visualiser.fxObjects.ChatHistory;
import seng302.visualiser.fxObjects.assets_2D.BoatObject;
import seng302.visualiser.fxObjects.assets_2D.WindArrow;
@@ -54,6 +62,16 @@ import seng302.visualiser.fxObjects.assets_2D.WindArrow;
*/
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
private final int CHAT_LIMIT = 128;
@FXML
private Pane basePane;
@FXML
private JFXButton chatSend;
@FXML
private Pane chatHistoryHolder;
@FXML
private TextField chatInput;
@FXML
private LineChart<String, Double> raceSparkLine;
@FXML
@@ -63,11 +81,12 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML
private CheckBox toggleFps;
@FXML
private Text timerLabel;
private Label timerLabel;
@FXML
private AnchorPane contentAnchorPane;
private StackPane contentAnchorPane;
private GridPane contentGridPane;
@FXML
private Text windDirectionText;
private AnchorPane rvAnchorPane;
@FXML
private AnchorPane windArrowHolder;
@FXML
@@ -79,7 +98,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML
private Text fpsDisplay;
@FXML
private Text windSpeedText;
private ImageView windImageView;
@FXML
private Label windDirectionLabel;
@FXML
private Label windSpeedLabel;
//Race Data
private Map<Integer, ClientYacht> participants;
@@ -88,31 +111,66 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private GameView3D gameView;
private RaceState raceState;
private ChatHistory chatHistory;
private Timeline timerTimeline;
private Timer timer = new Timer();
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
private ImportantAnnotationsState importantAnnotations;
private Polyline windArrow = new WindArrow(Color.LIGHTGRAY);
private ObservableList<ClientYacht> selectionComboBoxList = FXCollections.observableArrayList();
public void initialize() {
Sounds.stopMusic();
Sounds.playRaceMusic();
// Load a default important annotation state
importantAnnotations = new ImportantAnnotationsState();
//importantAnnotations = new ImportantAnnotationsState();
//Formatting the y axis of the sparkline
// raceSparkLine.getYAxis().setRotate(180);
// raceSparkLine.getYAxis().setTickLabelRotation(180);
// raceSparkLine.getYAxis().setTranslateX(-5);
raceSparkLine.visibleProperty().setValue(false);
raceSparkLine.getYAxis().setAutoRanging(false);
sparklineYAxis.setTickMarkVisible(false);
//raceSparkLine.visibleProperty().setValue(false);
//raceSparkLine.getYAxis().setAutoRanging(false);
//sparklineYAxis.setTickMarkVisible(false);
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
//positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// raceSparkLine.visibleProperty().setValue(false);
// raceSparkLine.getYAxis().setAutoRanging(false);
// sparklineYAxis.setTickMarkVisible(false);
// positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
windArrowHolder.getChildren().addAll(windArrow);
windArrow.setLayoutX(windArrowHolder.getWidth() / 2);
windArrow.setLayoutY(windArrowHolder.getHeight() / 2);
//selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
// rvAnchorPane.prefWidthProperty().bind(ViewManager.getInstance().getDecorator().widthProperty());
// rvAnchorPane.prefHeightProperty().bind(ViewManager.getInstance().getDecorator().heightProperty());
// selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
// windArrowHolder.getChildren().addAll(windArrow);
// windArrow.setLayoutX(windArrowHolder.getWidth() / 2);
// windArrow.setLayoutY(windArrowHolder.getHeight() / 2);
// selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
chatInput.lengthProperty().addListener((obs, oldLen, newLen) -> {
if (newLen.intValue() > CHAT_LIMIT) {
chatInput.setText(chatInput.getText().substring(0, CHAT_LIMIT));
}
});
chatHistory = new ChatHistory();
chatHistoryHolder.getChildren().addAll(chatHistory);
chatHistory.prefWidthProperty().bind(
chatHistoryHolder.widthProperty()
);
chatHistory.prefHeightProperty().bind(
chatHistoryHolder.heightProperty()
);
// chatHistory.setFitToWidth(true);
// chatHistory.setFitToHeight(true);
// chatHistory.textProperty().addListener((obs, oldValue, newValue) -> {
// chatHistory.setScrollTop(Double.MAX_VALUE);
// });
rvAnchorPane.setOnMouseClicked((event) ->
rvAnchorPane.requestFocus()
);
}
public void loadRace (
@@ -124,12 +182,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
this.markers = raceData.getCompoundMarks();
this.raceState = raceState;
initializeUpdateTimer();
initialiseFPSCheckBox();
initialiseAnnotationSlider();
initialiseBoatSelectionComboBox();
initialiseSparkLine();
raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> {
while (c.next()) {
if (c.wasPermutated()) {
@@ -142,7 +194,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
updateOrder(raceState.getPlayerPositions());
gameView = new GameView3D();
// gameView.setFrameRateFXText(fpsDisplay);
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView.getAssets()));
Platform.runLater(() -> {
contentAnchorPane.getChildren().add(0, gameView.getAssets());
((SubScene) gameView.getAssets()).widthProperty()
.bind(ViewManager.getInstance().getStage().widthProperty());
((SubScene) gameView.getAssets()).heightProperty()
.bind(ViewManager.getInstance().getStage().heightProperty());
System.out.println(((SubScene) gameView.getAssets()).getHeight());
System.out.println(((SubScene) gameView.getAssets()).getWidth());
});
gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit());
gameView.updateTokens(raceData.getTokens());
@@ -158,14 +219,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
gameView.setWindDir(newDirection.doubleValue());
Platform.runLater(() -> updateWindDirection(newDirection.doubleValue()));
});
// raceState.windSpeedProperty().addListener((obs, oldSpeed, newSpeed) ->
// Platform.runLater(() -> updateWindSpeed(newSpeed.doubleValue()))
// );
raceState.windSpeedProperty().addListener((obs, oldSpeed, newSpeed) ->
Platform.runLater(() -> updateWindSpeed(newSpeed.doubleValue()))
);
Platform.runLater(() -> {
updateWindDirection(raceState.windDirectionProperty().doubleValue());
updateWindSpeed(raceState.getWindSpeed());
});
gameView.setWindDir(raceState.windDirectionProperty().doubleValue());
Platform.runLater(() -> {
initializeUpdateTimer();
});
}
/**
@@ -213,40 +277,40 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
private void initialiseAnnotationSlider() {
annotationSlider.setLabelFormatter(new StringConverter<Double>() {
@Override
public String toString(Double n) {
if (n == 0) {
return "None";
}
if (n == 1) {
return "Important";
}
if (n == 2) {
return "All";
}
return "All";
}
@Override
public Double fromString(String s) {
switch (s) {
case "None":
return 0d;
case "Important":
return 1d;
case "All":
return 2d;
default:
return 2d;
}
}
});
annotationSlider.setValue(2);
annotationSlider.valueProperty().addListener((obs, oldVal, newVal) ->
setAnnotations((int) annotationSlider.getValue())
);
// annotationSlider.setLabelFormatter(new StringConverter<Double>() {
// @Override
// public String toString(Double n) {
// if (n == 0) {
// return "None";
// }
// if (n == 1) {
// return "Important";
// }
// if (n == 2) {
// return "All";
// }
// return "All";
// }
//
// @Override
// public Double fromString(String s) {
// switch (s) {
// case "None":
// return 0d;
// case "Important":
// return 1d;
// case "All":
// return 2d;
//
// default:
// return 2d;
// }
// }
// });
// annotationSlider.setValue(2);
// annotationSlider.valueProperty().addListener((obs, oldVal, newVal) ->
// setAnnotations((int) annotationSlider.getValue())
// );
}
@@ -254,52 +318,52 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* Used to add any new yachts into the race that may have started late or not have had data received yet
*/
private void updateSparkLine(){
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
// Collect the racing yachts that aren't already in the chart
sparkLineData.clear();
List<ClientYacht> sparkLineCandidates = new ArrayList<>(participants.values());
// Create a new data series for new yachts
sparkLineCandidates
.stream()
.filter(yacht -> yacht.getPosition() != null)
.forEach(yacht -> {
Series<String, Double> yachtData = new Series<>();
yachtData.setName(yacht.getSourceId().toString());
yachtData.getData().add(
new Data<>(
Integer.toString(yacht.getLegNumber()),
1.0 + participants.size() - yacht.getPosition()
)
);
sparkLineData.add(yachtData);
});
// Lambda function to sort the series in order of leg (later legs shown more to the right)
sparkLineData.sort((o1, o2) -> {
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
if (leg2 < leg1){
return 1;
} else {
return -1;
}
});
// Adds the new data series to the sparkline (and set the colour of the series)
Platform.runLater(() -> {
sparkLineData
.stream()
.filter(spark -> !raceSparkLine.getData().contains(spark))
.forEach(spark -> {
raceSparkLine.getData().add(spark);
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
});
});
// // TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
// // Collect the racing yachts that aren't already in the chart
// sparkLineData.clear();
// List<ClientYacht> sparkLineCandidates = new ArrayList<>(participants.values());
// // Create a new data series for new yachts
// sparkLineCandidates
// .stream()
// .filter(yacht -> yacht.getPosition() != null)
// .forEach(yacht -> {
// Series<String, Double> yachtData = new Series<>();
// yachtData.setName(yacht.getSourceId().toString());
// yachtData.getData().add(
// new Data<>(
// Integer.toString(yacht.getLegNumber()),
// 1.0 + participants.size() - yacht.getPosition()
// )
// );
// sparkLineData.add(yachtData);
// });
//
// // Lambda function to sort the series in order of leg (later legs shown more to the right)
// sparkLineData.sort((o1, o2) -> {
// Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
// Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
// if (leg2 < leg1){
// return 1;
// } else {
// return -1;
// }
// });
//
// // Adds the new data series to the sparkline (and set the colour of the series)
// Platform.runLater(() -> {
// sparkLineData
// .stream()
// .filter(spark -> !raceSparkLine.getData().contains(spark))
// .forEach(spark -> {
// raceSparkLine.getData().add(spark);
// spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
// });
// });
}
private void initialiseSparkLine() {
sparklineYAxis.setUpperBound(participants.size() + 1);
raceSparkLine.setCreateSymbols(false);
// sparklineYAxis.setUpperBound(participants.size() + 1);
// raceSparkLine.setCreateSymbols(false);
}
/**
@@ -316,13 +380,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
)
);
}
// XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
// positionData.getData().add(
// new XYChart.Data<>(
// Integer.toString(legNumber),
// 1.0 + participants.size() - yacht.getPlacing()
// )
// );
}
@@ -353,7 +410,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateRaceTime();
Platform.runLater(() -> updateRaceTime());
}
}, 0, 1000);
}
@@ -392,8 +449,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* @param direction the from north angle of the wind.
*/
private void updateWindDirection(double direction) {
windDirectionText.setText(String.format("%.1f°", direction));
windArrow.setRotate(direction);
windDirectionLabel.setText(String.format("%.1f°", direction));
windImageView.setRotate(direction);
}
/**
@@ -401,7 +458,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* @param windSpeed Windspeed in knots.
*/
private void updateWindSpeed(double windSpeed) {
windSpeedText.setText("Speed: " + String.format("%.1f", windSpeed) + " Knots");
windSpeedLabel.setText(String.format("%.1f", windSpeed) + " Knots");
}
@@ -409,12 +466,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* Updates the clock for the race
*/
private void updateRaceTime() {
// if (!raceState.isRaceStarted()) {
// timerLabel.setFill(Color.RED);
// timerLabel.setText("Race Finished!");
// } else {
timerLabel.setText(raceState.getRaceTimeStr());
// }
if (raceState.getTimeTillStart() <= 0L && !raceState.isRaceStarted()) {
timerLabel.setText("Race Finished!");
} else {
timerLabel.setText(raceState.getRaceTimeStr());
}
}
/**
@@ -422,28 +478,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* section
*/
private void updateOrder(ObservableList<ClientYacht> yachts) {
List<Text> vboxEntries = new ArrayList<>();
for (int i = 0; i < yachts.size(); i++) {
// System.out.println("yacht == null " + String.valueOf(yacht == null));
if (yachts.get(i).getBoatStatus() == BoatStatus.FINISHED
.getCode()) { // 3 is finish status
Text textToAdd = new Text(i + 1 + ". " +
yachts.get(i).getShortName() + " (Finished)");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
vboxEntries.add(textToAdd);
} else {
Text textToAdd = new Text(i + 1 + ". " +
yachts.get(i).getShortName() + " ");
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
textToAdd.setStyle("");
vboxEntries.add(textToAdd);
}
}
Platform.runLater(() ->
positionVbox.getChildren().setAll(vboxEntries)
);
// List<Text> vboxEntries = new ArrayList<>();
//
// for (int i = 0; i < yachts.size(); i++) {
//// System.out.println("yacht == null " + String.valueOf(yacht == null));
// if (yachts.get(i).getBoatStatus() == BoatStatus.FINISHED
// .getCode()) { // 3 is finish status
// Text textToAdd = new Text(i + 1 + ". " +
// yachts.get(i).getShortName() + " (Finished)");
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// vboxEntries.add(textToAdd);
//
// } else {
// Text textToAdd = new Text(i + 1 + ". " +
// yachts.get(i).getShortName() + " ");
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// textToAdd.setStyle("");
// vboxEntries.add(textToAdd);
// }
// }
// Platform.runLater(() ->
// positionVbox.getChildren().setAll(vboxEntries)
// );
}
@@ -544,15 +600,24 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* for the combobox to take action upon selection
*/
private void initialiseBoatSelectionComboBox() {
yachtSelectionComboBox.setItems(
FXCollections.observableArrayList(participants.values())
);
//Null check is if the listener is fired but nothing selected
yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
if (selectedBoat != null) {
// yachtSelectionComboBox.setItems(
// FXCollections.observableArrayList(participants.values())
// );
// //Null check is if the listener is fired but nothing selected
// yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
// if (selectedBoat != null) {
// gameView.selectBoat(selectedBoat);
}
});
// }
// });
//TODO uncomment out
// selectionComboBoxList.setAll(participants.values());
// yachtSelectionComboBox.setItems(selectionComboBoxList);
// yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
// if (selectedBoat != null) {
// gameView.selectBoat(selectedBoat);
// }
// });
}
/**
@@ -562,9 +627,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml"));
try {
contentAnchorPane.getChildren().removeAll();
contentAnchorPane.getChildren().clear();
contentAnchorPane.getChildren().addAll((Pane) loader.load());
contentGridPane.getChildren().removeAll();
contentGridPane.getChildren().clear();
contentGridPane.getChildren().addAll((Pane) loader.load());
} catch (javafx.fxml.LoadException e) {
System.err.println(e.getCause().toString());
@@ -629,8 +694,28 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
// }
}
public void updateRaceData (RaceXMLData raceData) {
gameView.updateBorder(raceData.getCourseLimit());
public void updateTokens(RaceXMLData raceData) {
gameView.updateTokens(raceData.getTokens());
}
public ReadOnlyBooleanProperty getSendPressedProperty() {
return chatSend.pressedProperty();
}
public boolean isChatInputFocused() {
// return chatInput.focusedProperty().getValue();
return false;
}
public String readChatInput() {
String chat = chatInput.getText();
chatInput.clear();
rvAnchorPane.requestFocus();
return chat;
}
public void updateChatHistory(Paint playerColour, String newMessage) {
Platform.runLater(() -> chatHistory.addMessage(playerColour, newMessage));
}
}
@@ -0,0 +1,201 @@
package seng302.visualiser.controllers;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialog.DialogTransition;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds;
import seng302.visualiser.ServerListener;
import seng302.visualiser.ServerListenerDelegate;
import seng302.visualiser.controllers.cells.ServerCell;
import seng302.visualiser.validators.HostNameFieldValidator;
import seng302.visualiser.validators.NumberRangeValidator;
import seng302.visualiser.validators.ValidationTools;
public class ServerListController implements Initializable, ServerListenerDelegate {
//--------FXML BEGIN--------//
// Layout Related
@FXML
private VBox serverListVBox;
@FXML
private ScrollPane serverListScrollPane;
@FXML
private StackPane serverListMainStackPane;
// Host Button
@FXML
private JFXButton serverListHostButton;
//Direct Connect
@FXML
private JFXButton connectButton;
@FXML
private JFXTextField serverHostName;
@FXML
private JFXTextField serverPortNumber;
//---------FXML END---------//
private Label noServersFound;
private Logger logger = LoggerFactory.getLogger(ServerListController.class);
public void initialize(URL location, ResourceBundle resources) {
serverListVBox.minWidthProperty().bind(serverListScrollPane.widthProperty());
// Set Event Bindings
connectButton.setOnMouseReleased(event -> {
attemptToDirectConnect();
Sounds.playButtonClick();
});
for (JFXTextField textField : Arrays.asList(serverHostName, serverPortNumber)) {
// Event for pressing enter to submit direct connection
textField.setOnKeyPressed(event -> {
if (event.getCode().equals(KeyCode.ENTER)) {
attemptToDirectConnect();
}
});
// Validators as empty fields are invalid.
RequiredFieldValidator validator = new RequiredFieldValidator();
validator.setMessage("Field is Required");
textField.getValidators().add(validator);
}
// Validating the hostname
HostNameFieldValidator hostNameValidator = new HostNameFieldValidator();
hostNameValidator.setMessage("Host name incorrect");
serverHostName.getValidators().add(hostNameValidator);
// Validating the port number
NumberRangeValidator portNumberValidator = new NumberRangeValidator(1025, 65536);
portNumberValidator.setMessage("Port number incorrect");
serverPortNumber.getValidators().add(portNumberValidator);
// Start listening for servers on network
try {
ServerListener.getInstance().setDelegate(this);
} catch (IOException e) {
logger.warn("Could not start Server Listener Delegate");
}
// Create Label for no servers found.
noServersFound = new Label();
noServersFound.minWidthProperty().bind(serverListVBox.widthProperty());
noServersFound.setAlignment(Pos.CENTER);
noServersFound.setText("No Servers Found");
noServersFound.setStyle(
"-fx-font-size: 30px;"
+ "-fx-padding:50px;"
+ "-fx-text-fill: -fx-pp-dark-text-color;"
);
serverListVBox.getChildren().add(noServersFound);
// Set up dialog for server creation
Platform.runLater(() -> {
FXMLLoader dialogContent = new FXMLLoader(getClass().getResource(
"/views/dialogs/ServerCreationDialog.fxml"));
try {
JFXDialog dialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
DialogTransition.CENTER);
serverListHostButton.setOnAction(action -> {
dialog.show();
Sounds.playButtonClick();
});
} catch (IOException e) {
logger.warn("Could not create Server Creation Dialog.");
}
});
}
/**
*
*/
private void attemptToDirectConnect() {
if (validateDirectConnection(serverHostName.getText(), serverPortNumber.getText())) {
DirectConnect();
}
}
/**
*
* @param hostName
* @param portNumber
* @return
*/
private Boolean validateDirectConnection(String hostName, String portNumber) {
Boolean hostNameValid = ValidationTools.validateTextField(serverHostName);
Boolean portNumberValid = ValidationTools.validateTextField(serverPortNumber);
return hostNameValid && portNumberValid;
}
/**
*
*/
private void DirectConnect() {
Sounds.playButtonClick();
ViewManager.getInstance().getGameClient().runAsClient(serverHostName.getText(), Integer.parseInt(serverPortNumber.getText()));
}
/**
*
* @param servers
*/
private void refreshServers(List<ServerDescription> servers) {
serverListVBox.getChildren().clear();
if (servers.size() == 0) { // "No Servers Found"
serverListVBox.getChildren().add(noServersFound);
} else { // Populate the server list with a series of server cell objects.
for (ServerDescription server : servers) {
VBox pane = null;
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/views/cells/ServerCell.fxml"));
loader.setController(new ServerCell(server));
try {
pane = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
serverListVBox.getChildren().add(pane);
}
}
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
@Override
public void serverRemoved(List<ServerDescription> servers) {
Platform.runLater(() -> refreshServers(servers));
}
@Override
public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) {
Platform.runLater(() -> refreshServers(servers));
}
}
@@ -1,168 +1,109 @@
package seng302.visualiser.controllers;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import com.jfoenix.controls.JFXButton;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import seng302.gameServer.GameState;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient;
/**
* A Class describing the actions of the start screen controller
* Created by wmu16 on 10/07/17.
*/
public class StartScreenController implements Initializable {
public class StartScreenController implements Initializable{
//--------FXML BEGIN--------//
@FXML
private TextField ipTextField;
private Label headText;
@FXML
private TextField portTextField;
@FXML
private GridPane startScreen2;
@FXML
private AnchorPane holder;
GameClient gameClient;
public void initialize(URL url, ResourceBundle resourceBundle) {
// gameClient = new GameClient(holder);
}
//
// /**
// * Loads the fxml content into the parent pane
// * @param jfxUrl
// * @return the controller of the fxml
// */
// private Object setContentPane(String jfxUrl) {
// try {
// AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
// contentPane.getChildren().removeAll();
// contentPane.getChildren().clear();
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
// contentPane.getChildren().addAll((Pane) fxmlLoader.load());
//
// return fxmlLoader.getController();
// } catch (IOException e) {
// e.printStackTrace();
// }
// return null;
// }
private JFXButton startBtn;
//---------FXML END---------//
private Node serverList;
private Logger logger = LoggerFactory.getLogger(StartScreenController.class);
private List<ServerDescription> servers;
private GameClient gameClient;
/**
* ATTEMPTS TO:
* Sets up a new game state with your IP address as designated as the host.
* Starts a thread to listen for incoming connections.
* Starts a client to server thread and connects to own ip.
* Switches to the lobby screen
*/
@FXML
public void hostButtonPressed() {
// new GameState(getLocalHostIp());
gameClient = new GameClient(holder);
gameClient.runAsHost(getLocalHostIp(), 4942);
// try {
//// String ipAddress = InetAddress.getLocalHost().getHostAddress();
//// new GameState(ipAddress);
//// new MainServerThread();
//// ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
//// controller.setClientToServerThread(clientToServerThread);
// // get the lobby controller so that we can pass the game server thread to it
// new GameState(getLocalHostIp());
// MainServerThread mainServerThread = new MainServerThread();
//// ClientState.setHost(true);
// // host will connect and handshake to itself after setting up the server
// // TODO: 24/07/17 wmu16 - Make port number some static global type constant?
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
//// ClientState.setConnectedToHost(true);
//// controller.setClientToServerThread(clientToServerThread);
// LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
// lobbyController.setMainServerThread(mainServerThread);
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot host");
// alert.setContentText("Oops, failed to host, try to restart.");
// alert.showAndWait();
// e.printStackTrace();
// }
}
/**
* ATTEMPTS TO:
* Connect to an ip address and port using the ip and port specified on start screen.
* Starts a Client To Server Thread to maintain connection to host.
* Switch view to lobby view.
*/
@FXML
public void connectButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function
gameClient = new GameClient(holder);
gameClient.runAsClient(ipTextField.getText().trim().toLowerCase(), 4942);
// try {
// String ipAddress = ipTextField.getText().trim().toLowerCase();
// Integer port = Integer.valueOf(portTextField.getText().trim());
//
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
//// ClientState.setHost(false);
//// ClientState.setConnectedToHost(true);
//
//// controller.setClientToServerThread(clientToServerThread);
//// setContentPane("/views/LobbyView.fxml");
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot reach the host");
// alert.setContentText("Please check your host IP address.");
// alert.showAndWait();
// }
}
// public void setController(Controller controller) {
// this.controller = controller;
// }
/**
* Gets the local host ip address and sets this ip to ClientState.
* Only runs by the host.
*
* @return the localhost ip address
*/
private String getLocalHostIp() {
String ipAddress = null;
try {
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
while (e.hasMoreElements()) {
NetworkInterface ni = e.nextElement();
if (ni.isLoopback())
continue;
if(ni.isPointToPoint())
continue;
if(ni.isVirtual())
continue;
private void setInitialDropShadow() {
DropShadow dropShadow = new DropShadow();
dropShadow.setRadius(10.0);
dropShadow.setOffsetX(3.0);
dropShadow.setOffsetY(4.0);
dropShadow.setColor(Color.color(0, 0, 0, 0.5));
headText.setEffect(dropShadow);
}
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while(addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if(address instanceof Inet4Address) { // skip all ipv6
ipAddress = address.getHostAddress();
}
}
}
/**
*
*/
private void preloadServerListView(){
try {
serverList = FXMLLoader
.load(StartScreenController.class.getResource("/views/ServerListView.fxml"));
} catch (IOException e) {
e.printStackTrace();
logger.error("Could not preload server list view");
}
}
/**
*
*/
private void goToServerBrowser() {
try {
ViewManager.getInstance().setScene(serverList);
} catch (Exception e) {
e.printStackTrace();
}
if (ipAddress == null) {
System.out.println("[HOST] Cannot obtain local host ip address.");
}
public void initialize(URL location, ResourceBundle resources) {
startBtn.setOnMousePressed(event -> {
startBtn.setText("LOADING...");
Sounds.playButtonClick();
});
startBtn.setOnMouseReleased(event -> goToServerBrowser());
setInitialDropShadow();
preloadServerListView();
}
public void toggleMusic(ActionEvent actionEvent) {
Sounds.toggleMuteMusic();
Sounds.playButtonClick();
if (Sounds.isMusicMuted()) {
// muteMusicButton.setText("UnMute Music");
} else {
// muteMusicButton.setText("Mute Music");
}
// ClientState.setHostIp(ipAddress);
return ipAddress;
}
public void toggleSounds(ActionEvent actionEvent) {
Sounds.toggleMuteEffects();
Sounds.playButtonClick();
if (Sounds.isSoundEffectsMuted()) {
// muteSoundsButton.setText("UnMute Sounds");
} else {
// muteSoundsButton.setText("Mute Sounds");
}
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}
@@ -0,0 +1,305 @@
package seng302.visualiser.controllers;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDecorator;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.svg.SVGGlyph;
import java.io.IOException;
import java.util.HashMap;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.gameServer.ServerAdvertiser;
import seng302.utilities.BonjourInstallChecker;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient;
import seng302.visualiser.controllers.dialogs.BoatCustomizeController;
public class ViewManager {
private static ViewManager instance;
private GameClient gameClient;
private JFXDecorator decorator;
private HashMap<String, String> properties; //TODO is this the best way to do this??
private ObservableList<String> playerList;
private Logger logger = LoggerFactory.getLogger(ViewManager.class);
public Stage getStage() {
return stage;
}
private Stage stage;
private ViewManager() {
properties = new HashMap<>();
}
private FXMLLoader loadFxml(String fxmlLocation) {
return new FXMLLoader(
getClass().getResource(fxmlLocation)
);
}
public static ViewManager getInstance() {
if (instance == null) {
instance = new ViewManager();
}
return instance;
}
/**
* Initialize the start view in the given stage.
*/
public void initialStartView(Stage stage) throws Exception {
this.stage = stage;
Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml"));
stage.setTitle("Party Parrots At Sea");
JFXDecorator decorator = new JFXDecorator(stage, root, false, true, true);
decorator.setCustomMaximize(true);
decorator.applyCss();
decorator.getStylesheets()
.add(getClass().getResource("/css/Master.css").toExternalForm());
setDecorator(decorator);
gameClient = new GameClient(decorator);
stage.getIcons().add(new Image(getClass().getResourceAsStream("/PP.png")));
Scene scene = new Scene(decorator, 1200, 800, false, SceneAntialiasing.BALANCED);
stage.setMinHeight(800);
stage.setMinWidth(1200);
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(e -> closeAll());
decorator.setOnCloseButtonAction(this::closeAll);
// TODO Platform.runLater(this::checkCompatibility);
Sounds.stopMusic();
Sounds.playMenuMusic();
decorator.setOnCloseButtonAction(() -> {
try {
ServerAdvertiser.getInstance().unregister();
} catch (IOException e) {
logger.warn("Couldn't unregister server");
}
gameClient.stopGame();
System.exit(0);
});
}
private void setDecorator(JFXDecorator newDecorator) {
decorator = newDecorator;
//Injecting a volume toggle into the decorator.
//Get the button box
HBox btns = (HBox) decorator.getChildren().get(0);
//Create new button
JFXButton btnMute = new JFXButton();
btnMute.setText(" Toggle Sound");
btnMute.setStyle("-fx-text-fill:#fff");
btnMute.getStyleClass().add("jfx-decorator-button");
btnMute.setCursor(Cursor.HAND);
//Create Graphics
SVGGlyph spacer = new SVGGlyph(0, "SPACER", "", Color.WHITE);
SVGGlyph volumeOn = new SVGGlyph(0, "VOLUME_ON",
"M39.389,13.769 22.235,28.606 6,28.606 6,47.699 21.989,47.699 39.389,62.75 39.389,13.769 M 48.128,49.03 C 50.057,45.934 51.19,42.291 51.19,38.377 C 51.19,34.399 50.026,30.703 48.043,27.577 M 55.082,20.537 C 58.777,25.523 60.966,31.694 60.966,38.377 C 60.966,44.998 58.815,51.115 55.178,56.076 M 61.71,62.611 C 66.977,55.945 70.128,47.531 70.128,38.378 C 70.128,29.161 66.936,20.696 61.609,14.01",
Color.WHITE);
SVGGlyph volumeOff = new SVGGlyph(0, "VOLUME_ON",
"M39.389,13.769 22.235,28.606 6,28.606 6,47.699 21.989,47.699 39.389,62.75 39.389,13.769",
Color.WHITE);
volumeOn.setSize(16, 16);
volumeOff.setSize(12, 16);
spacer.setSize(40, 16);
// Determine which graphic should go on the button
if (Sounds.isMusicMuted() && Sounds.isSoundEffectsMuted()) {
btnMute.setGraphic(volumeOff);
} else {
btnMute.setGraphic(volumeOn);
}
// Add Buttons
btns.getChildren().add(0, spacer);
btns.getChildren().add(0, btnMute);
btnMute.setOnAction((action) -> {
Sounds.toggleAllSounds();
if (btnMute.getGraphic().equals(volumeOff)) {
btnMute.setGraphic(volumeOn);
} else {
btnMute.setGraphic(volumeOff);
}
});
}
private void checkCompatibility() {
if (BonjourInstallChecker.isBonjourSupported()) {
BonjourInstallChecker.openInstallUrl();
}
}
private void closeAll() {
try {
ServerAdvertiser.getInstance().unregister();
} catch (IOException e1) {
logger.warn("Could not un-register game");
}
System.exit(0);
}
public JFXDecorator getDecorator() {
return decorator;
}
public void setScene(Node scene) {
Platform.runLater(() -> decorator.setContent(scene));
}
/**
* Create a new stage and re-initialize the start view in the new stage.
*/
public void goToStartView() {
try {
this.stage.close();
Stage stage = new Stage();
initialStartView(stage);
} catch (Exception e) {
e.printStackTrace();
}
}
public GameClient getGameClient() {
return gameClient;
}
public String getProperty(String key) {
return properties.get(key);
}
public void setProperty(String key, String val) {
properties.put(key, val);
}
public void setPlayerList(ObservableList<String> playerList) {
this.playerList = playerList;
}
public ObservableList<String> getPlayerList() {
return playerList;
}
public LobbyController goToLobby(Boolean disableReadyButton) {
FXMLLoader loader = loadFxml("/views/LobbyView.fxml");
try {
setScene(loader.load());
} catch (IOException e) {
logger.error("Could not load lobby view");
}
if (disableReadyButton) {
LobbyController lobbyController = loader.getController();
lobbyController.disableReadyButton();
}
return loader.getController();
}
public RaceViewController loadRaceView() {
FXMLLoader loader = loadFxml("/views/RaceView.fxml");
// have to create a new stage and set the race view maximized as JFoenix decorator has
// bug causes stage cannot be fully maximised.
Platform.runLater(() -> {
try {
stage.close();
stage = new Stage();
JFXDecorator decorator = new JFXDecorator(stage, loader.load(), false, true, true);
decorator.setCustomMaximize(true);
decorator.applyCss();
decorator.getStylesheets()
.add(getClass().getResource("/css/Master.css").toExternalForm());
setDecorator(decorator);
Scene scene = new Scene(decorator);
// set key press event to catch key stoke
scene.setOnKeyPressed(gameClient::keyPressed);
scene.setOnKeyReleased(gameClient::keyReleased);
// uncomment to make it full screen
// Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
// stage.setX(visualBounds.getMinX());
// stage.setY(visualBounds.getMinY());
// stage.setWidth(visualBounds.getWidth());
// stage.setHeight(visualBounds.getHeight());
// stage.setMaximized(true);
// stage.setFullScreen(true);
stage.setMinHeight(500);
stage.setMinWidth(800);
stage.setOnCloseRequest(e -> closeAll());
stage.setScene(scene);
stage.show();
} catch (Exception e) {
e.printStackTrace();
}
});
while (loader.getController() == null){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return loader.getController();
}
public JFXDialog loadCustomizationDialog(StackPane parent, LobbyController lobbyController,
Color playerColor, String name) {
FXMLLoader dialog = loadFxml("/views/dialogs/BoatCustomizeDialog.fxml");
JFXDialog customizationDialog = null;
try {
customizationDialog = new JFXDialog(parent, dialog.load(),
JFXDialog.DialogTransition.CENTER);
} catch (IOException e) {
e.printStackTrace();
}
BoatCustomizeController controller = dialog.getController();
controller.setParentController(lobbyController);
controller.setPlayerColor(playerColor);
controller.setPlayerName(name);
controller.setServerThread(gameClient.getServerThread());
controller.setPlayerColor(lobbyController.playersColor);
return customizationDialog;
}
}
@@ -0,0 +1,60 @@
package seng302.visualiser.controllers.cells;
import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import seng302.utilities.Sounds;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import seng302.visualiser.fxObjects.assets_3D.BoatModel;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
public class PlayerCell {
//--------FXML BEGIN--------//
@FXML
private Label playerName;
@FXML
private GridPane playerListCell;
@FXML
private Pane boatPane;
//---------FXML END---------//
private String name;
private Color boatColor;
private Integer playerId;
public PlayerCell(Integer playerId, String playerName, Color color) {
this.playerId = playerId;
this.name = playerName;
this.boatColor = color;
}
public void initialize() {
playerName.setText(name);
Group group = new Group();
boatPane.getChildren().add(group);
BoatModel bo = ModelFactory.boatIconView(BoatMeshType.DINGHY, this.boatColor);
group.getChildren().add(bo.getAssets());
}
public Integer getPlayerId() {
return playerId;
}
public String getPlayerName() {
return name;
}
public Color getBoatColor() {
return boatColor;
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}
@@ -0,0 +1,86 @@
package seng302.visualiser.controllers.cells;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDecorator;
import com.sun.org.apache.bcel.internal.classfile.Unknown;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ResourceBundle;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient;
import seng302.visualiser.controllers.ViewManager;
public class ServerCell implements Initializable {
//--------FXML BEGIN--------//
//Layout
@FXML
private GridPane serverListCell;
//Server Information
@FXML
private Label serverName;
@FXML
private Label mapName;
@FXML
private Label serverPlayerCount;
//Server Connection
@FXML
private JFXButton serverConnButton;
//---------FXML END---------//
private String name;
private String mapNameString;
private String currPlayerCount;
private String hostName;
private Integer portNumber;
public ServerCell(ServerDescription server) {
this.name = server.getName();
this.currPlayerCount = server.getNumPlayers().toString() + "/" + server.getCapacity().toString();
this.mapNameString = server.getMapName();
// Can cause issues on windows PCs without the bonjour service installed.
this.hostName = server.getAddress();
this.portNumber = server.portNumber();
}
public void initialize(URL location, ResourceBundle resources) {
serverName.setText(name);
serverPlayerCount.setText(currPlayerCount);
mapName.setText(mapNameString);
serverConnButton.setOnMouseReleased(event -> {
Sounds.playButtonClick();
joinServer();
});
}
/**
*
*/
private void joinServer() {
System.out.println("Connecting to " + serverName.getText());
ViewManager.getInstance().getGameClient().runAsClient(hostName, portNumber);
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}
@@ -0,0 +1,98 @@
package seng302.visualiser.controllers.dialogs;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXColorPicker;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import seng302.gameServer.messages.CustomizeRequestType;
import seng302.utilities.Sounds;
import seng302.visualiser.ClientToServerThread;
import java.net.URL;
import java.util.ResourceBundle;
import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.validators.FieldLengthValidator;
import seng302.visualiser.validators.ValidationTools;
public class BoatCustomizeController implements Initializable{
//--------FXML BEGIN--------//
@FXML
private JFXColorPicker colorPicker;
@FXML
private JFXButton submitBtn;
@FXML
private JFXTextField boatName;
@FXML
void colorChanged(ActionEvent event) {
Color color = colorPicker.getValue();
}
//---------FXML END---------//
private ClientToServerThread socketThread;
private LobbyController lobbyController;
@Override
public void initialize(URL location, ResourceBundle resources) {
submitBtn.setOnMouseReleased(event -> {
Sounds.playButtonClick();
submitCustomization();
});
RequiredFieldValidator playerNameReqValidator = new RequiredFieldValidator();
playerNameReqValidator.setMessage("Player name required.");
FieldLengthValidator playerNameLengthValidator = new FieldLengthValidator(20);
playerNameLengthValidator.setMessage("Player name too long.");
boatName.setValidators(playerNameLengthValidator, playerNameReqValidator);
submitBtn.setOnMouseEntered(e -> Sounds.playHoverSound());
}
/**
*
*/
private void submitCustomization() {
if (ValidationTools.validateTextField(boatName)) {
socketThread
.sendCustomizationRequest(CustomizeRequestType.NAME, boatName.getText().getBytes());
Color color = colorPicker.getValue();
short red = (short) (color.getRed() * 255);
short green = (short) (color.getGreen() * 255);
short blue = (short) (color.getBlue() * 255);
byte[] colorArray = new byte[3];
colorArray[0] = (byte) red;
colorArray[1] = (byte) green;
colorArray[2] = (byte) blue;
socketThread.sendCustomizationRequest(CustomizeRequestType.COLOR, colorArray);
lobbyController.closeCustomizationDialog();
}
}
public void setPlayerName(String name) {
this.boatName.setText(name);
}
public void setPlayerColor(Color playerColor) {
this.colorPicker.setValue(playerColor);
}
public void setServerThread(ClientToServerThread ctsThread) {
this.socketThread = ctsThread;
}
public void setParentController(LobbyController lobbyController){
this.lobbyController = lobbyController;
}
}
@@ -0,0 +1,89 @@
package seng302.visualiser.controllers.dialogs;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSlider;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds;
import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.validators.FieldLengthValidator;
import java.net.URL;
import java.util.ResourceBundle;
import seng302.visualiser.validators.ValidationTools;
public class ServerCreationController implements Initializable {
//--------FXML BEGIN--------//
@FXML
private JFXTextField serverName;
@FXML
private JFXSlider maxPlayersSlider;
@FXML
private Label maxPlayersLabel;
@FXML
private JFXButton submitBtn;
//---------FXML END---------//
public void initialize(URL location, ResourceBundle resources) {
updateMaxPlayerLabel();
maxPlayersSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
updateMaxPlayerLabel();
});
FieldLengthValidator fieldLengthValidator = new FieldLengthValidator(40);
fieldLengthValidator.setMessage("Server name too long.");
RequiredFieldValidator fieldRequiredValidator = new RequiredFieldValidator();
fieldRequiredValidator.setMessage("Server name is required.");
serverName.setValidators(fieldLengthValidator, fieldRequiredValidator);
submitBtn.setOnMouseReleased(event -> {
Sounds.playButtonClick();
validateServerSettings();
});
}
/**
*
*/
private void validateServerSettings() {
submitBtn.setText("CREATING...");
if (ValidationTools.validateTextField(serverName)) {
createServer();
} else {
submitBtn.setText("SUBMIT");
}
}
/**
*
*/
private void createServer() {
ServerDescription serverDescription = ViewManager.getInstance().getGameClient()
.runAsHost("localhost", 4941, serverName.getText(), (int) maxPlayersSlider
.getValue());
ViewManager.getInstance().setProperty("serverName", serverDescription.getName());
ViewManager.getInstance().setProperty("mapName", serverDescription.getMapName());
}
/**
*
*/
private void updateMaxPlayerLabel() {
maxPlayersSlider.setValue(Math.floor(maxPlayersSlider.getValue()));
maxPlayersLabel.setText(String.format("YOU SELECTED: %.0f", maxPlayersSlider.getValue()));
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
}