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
@@ -18,6 +18,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatActionMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestMessage;
import seng302.gameServer.messages.CustomizeRequestType;
@@ -281,9 +282,17 @@ public class ClientToServerThread implements Runnable {
* @param message The given message type.
*/
private void sendBoatActionMessage(BoatActionMessage message) {
sendByteBuffer(message.getBuffer());
}
public void sendChatterMessage(String message) {
sendByteBuffer(new ChatterMessage(clientId, message).getBuffer());
}
private void sendByteBuffer(byte[] bytes) {
if (clientId != -1) {
try {
os.write(message.getBuffer());
os.write(bytes);
} catch (IOException e) {
logger.warn("IOException on attempting to sendBoatAction from Client");
notifyDisconnectListeners("Cannot communicate with server");
@@ -292,7 +301,7 @@ public class ClientToServerThread implements Runnable {
}
}
private void closeSocket() {
public void closeSocket() {
try {
socket.close();
socketOpen = false;
@@ -357,7 +366,7 @@ public class ClientToServerThread implements Runnable {
}
}
int getClientId () {
public int getClientId () {
return clientId;
}
}
+167 -66
View File
@@ -1,8 +1,13 @@
package seng302.visualiser;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import javafx.application.Platform;
@@ -12,12 +17,17 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.util.Pair;
import seng302.gameServer.GameStages;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import seng302.gameServer.ServerDescription;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatStatus;
import seng302.gameServer.messages.YachtEventType;
import seng302.model.ClientYacht;
import seng302.model.RaceState;
import seng302.model.stream.packets.StreamPacket;
@@ -28,12 +38,15 @@ import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.parser.YachtEventData;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.utilities.Sounds;
import seng302.utilities.StreamParser;
import seng302.utilities.XMLGenerator;
import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.FinishScreenViewController;
import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.controllers.LobbyController.CloseStatus;
import seng302.visualiser.controllers.LobbyController_old;
import seng302.visualiser.controllers.RaceViewController;
import seng302.visualiser.controllers.ViewManager;
/**
* This class is a client side instance of a yacht racing game in JavaFX. The game is instantiated
@@ -53,6 +66,8 @@ public class GameClient {
private RaceState raceState = new RaceState();
private LobbyController lobbyController;
private ArrayList<ClientYacht> finishedBoats = new ArrayList<>();
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
/**
@@ -62,6 +77,17 @@ public class GameClient {
*/
public GameClient(Pane holder) {
this.holderPane = holder;
// if (holderPane.getParent() == null) {
// this.holderPane.parentProperty().addListener(((observable, oldValue, newValue) -> {
// if (newValue != null) {
// newValue.getScene().setOnKeyPressed(this::keyPressed);
// newValue.getScene().setOnKeyReleased(this::keyReleased);
// }
// }));
// } else {
// this.holderPane.getParent().getScene().setOnKeyPressed(this::keyPressed);
// this.holderPane.getParent().getScene().setOnKeyReleased(this::keyReleased);
// }
}
/**
@@ -78,28 +104,36 @@ public class GameClient {
Platform.runLater(this::loadStartScreen);
});
socketThread.addStreamObserver(this::parsePackets);
LobbyController lobbyController = loadLobby();
lobbyController.setSocketThread(socketThread);
lobbyController.setPlayerID(socketThread.getClientId());
lobbyController.setPlayerListSource(clientLobbyList);
lobbyController.disableReadyButton();
if (regattaData != null){
lobbyController.setTitle(regattaData.getRegattaName());
lobbyController.setCourseName(regattaData.getCourseName());
}
else{
lobbyController.setTitle(ipAddress);
lobbyController.setCourseName("");
ViewManager.getInstance().setPlayerList(clientLobbyList);
while (regattaData == null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lobbyController.addCloseListener((exitCause) -> {
this.tearDownConnection();
this.loadStartScreen();
});
this.lobbyController = lobbyController;
ViewManager.getInstance().setProperty("serverName", regattaData.getRegattaName());
ViewManager.getInstance().setProperty("mapName", regattaData.getCourseName());
// TODO disable ready button;
//LobbyController_old lobbyController = loadLobby();
//lobbyController.setSocketThread(socketThread);
//lobbyController.setPlayerID(socketThread.getClientId());
//lobbyController.setPlayerListSource(clientLobbyList);
//lobbyController.disableReadyButton();
// lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
this.lobbyController = ViewManager.getInstance().goToLobby(true);
} catch (IOException ioe) {
showConnectionError("Unable to find server");
Platform.runLater(this::loadStartScreen);
//Platform.runLater(this::loadStartScreen);
}
}
@@ -108,41 +142,30 @@ public class GameClient {
* @param ipAddress IP to connect to.
* @param portNumber Port to connect to.
*/
public void runAsHost(String ipAddress, Integer portNumber) {
server = new MainServerThread();
try {
startClientToServerThread(ipAddress, portNumber);
socketThread.addDisconnectionListener((cause) -> {
this.tearDownConnection();
Platform.runLater(this::loadStartScreen);
});
LobbyController lobbyController = loadLobby();
lobbyController.setSocketThread(socketThread);
lobbyController.setPlayerID(socketThread.getClientId());
lobbyController.setPlayerListSource(clientLobbyList);
if (regattaData != null) {
lobbyController.setTitle("Hosting: " + regattaData.getRegattaName());
lobbyController.setCourseName(regattaData.getCourseName());
} else {
lobbyController.setTitle("Hosting: " + ipAddress);
lobbyController.setCourseName("");
}
public ServerDescription runAsHost(String ipAddress, Integer portNumber, String serverName, Integer maxPlayers) {
XMLGenerator.setDefaultRaceName(serverName);
GameState.setMaxPlayers(maxPlayers);
lobbyController.addCloseListener(exitCause -> {
if (exitCause == CloseStatus.READY) {
GameState.resetStartTime();
lobbyController.disableReadyButton();
server.startGame();
} else if (exitCause == CloseStatus.LEAVE) {
tearDownConnection();
loadStartScreen();
}
});
this.lobbyController = lobbyController;
} catch (IOException ioe) {
server = new MainServerThread();
try {
startClientToServerThread(ipAddress, 4942);
} catch (IOException e) {
showConnectionError("Cannot connect to server as host");
Platform.runLater(this::loadStartScreen);
}
while (regattaData == null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.lobbyController = ViewManager.getInstance().goToLobby(false);
ViewManager.getInstance().setPlayerList(clientLobbyList);
return new ServerDescription(serverName, regattaData.getCourseName(), GameState.getNumberOfPlayers(), GameState.getCapacity(), ipAddress, 4942);
}
private void tearDownConnection() {
@@ -183,7 +206,7 @@ public class GameClient {
*
* @return the lobby controller.
*/
private LobbyController loadLobby() {
private LobbyController_old loadLobby() {
FXMLLoader fxmlLoader = new FXMLLoader(
GameClient.class.getResource("/views/LobbyView.fxml"));
try {
@@ -202,9 +225,15 @@ public class GameClient {
raceView = fxmlLoader.getController();
ClientYacht player = allBoatsMap.get(socketThread.getClientId());
raceView.loadRace(allBoatsMap, courseData, raceState, player);
}
private void loadFinishScreenView() {
Sounds.stopMusic();
Sounds.stopSoundEffects();
Sounds.playFinishMusic();
FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml");
FinishScreenViewController controller = fxmlLoader.getController();
controller.setFinishers(raceState.getPlayerPositions());
@@ -258,8 +287,8 @@ public class GameClient {
if (courseData == null) { //workaround for object comparisons. Avoid recreating
courseData = raceXMLData;
}
if (raceView != null) {
raceView.updateRaceData(raceXMLData);
if (raceView != null) { //Token update
raceView.updateTokens(raceXMLData);
}
break;
@@ -272,11 +301,14 @@ public class GameClient {
clientLobbyList.add(boat.getBoatName())
);
raceState.setBoats(allBoatsMap.values());
if (lobbyController != null) {
lobbyController.setBoats(allBoatsMap);
}
break;
case RACE_START_STATUS:
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
if (lobbyController != null) lobbyController.updateRaceState(raceState);
if (lobbyController != null) Platform.runLater(() -> lobbyController.updateRaceState(raceState));
break;
case BOAT_LOCATION:
@@ -288,15 +320,37 @@ public class GameClient {
break;
case YACHT_EVENT_CODE:
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
YachtEventData yachtEventData = StreamParser.extractYachtEventCode(packet);
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN.getCode()) {
showPickUp();
}
break;
case CHATTER_TEXT:
Pair<Integer, String> playerIdMessagePair = StreamParser
.extractChatterText(packet);
raceView.updateChatHistory(
allBoatsMap.get(playerIdMessagePair.getKey()).getColour(),
playerIdMessagePair.getValue()
);
}
}
}
private void startRaceIfAllDataReceived() {
if (allXMLReceived() && raceView == null) {
loadRaceView();
raceView = ViewManager.getInstance().loadRaceView();
ClientYacht player = allBoatsMap.get(socketThread.getClientId());
raceView.loadRace(allBoatsMap, courseData, raceState, player);
raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> {
if (isPressed) {
formatAndSendChatMessage(raceView.readChatInput());
}
});
}
}
@@ -330,7 +384,6 @@ public class GameClient {
if (allXMLReceived()) {
ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId());
clientYacht.roundMark(
courseData.getCompoundMarks().get(roundingData.getMarkId()),
roundingData.getTimeStamp(),
raceState.getRaceTime() - roundingData.getTimeStamp()
);
@@ -345,6 +398,9 @@ public class GameClient {
for (ClientYacht yacht : allBoatsMap.values()) {
if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) {
raceFinished = false;
} else if (!finishedBoats.contains(yacht)) {
finishedBoats.add(yacht);
Sounds.playFinishSound();
}
}
@@ -360,6 +416,7 @@ public class GameClient {
}
if (raceFinished) {
Sounds.playFinishSound();
close();
loadFinishScreenView();
}
@@ -382,7 +439,13 @@ public class GameClient {
* Handle the key-pressed event from the text field.
* @param e The key event triggering this call
*/
private void keyPressed(KeyEvent e) {
public void keyPressed(KeyEvent e) {
if (raceView.isChatInputFocused()) {
if (e.getCode() == KeyCode.ENTER) {
formatAndSendChatMessage(raceView.readChatInput());
}
return;
}
switch (e.getCode()) {
case SPACE: // align with vmg
socketThread.sendBoatAction(BoatAction.VMG); break;
@@ -391,12 +454,16 @@ public class GameClient {
case PAGE_DOWN: // downwind
socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
case ENTER: // tack/gybe
// if chat box is active take whatever is in there and send it to server
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
}
}
private void keyReleased(KeyEvent e) {
public void keyReleased(KeyEvent e) {
if (raceView.isChatInputFocused()) {
return;
}
switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
case SHIFT: // sails in/sails out
@@ -417,13 +484,47 @@ public class GameClient {
* Tells race view to show a collision animation.
*/
private void showCollisionAlert(YachtEventData yachtEventData) {
// 33 is the agreed code to show collision
if (yachtEventData.getEventId() == 33) {
raceState.storeCollision(
allBoatsMap.get(
yachtEventData.getSubjectId().intValue()
)
Sounds.playCrashSound();
raceState.storeCollision(
allBoatsMap.get(
yachtEventData.getSubjectId().intValue()
)
);
}
// TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user
private void showPickUp() {
Sounds.playTokenPickupSound();
}
private void formatAndSendChatMessage(String rawChat) {
if (rawChat.length() > 0) {
socketThread.sendChatterMessage(
new SimpleDateFormat("[HH:mm:ss] ").format(new Date()) +
allBoatsMap.get(socketThread.getClientId()).getShortName() + ": " + rawChat
);
}
}
public void startGame(){
server.startGame();
}
public ClientToServerThread getServerThread() {
return socketThread;
}
public List<String> getPlayerNames(){
return Collections.unmodifiableList(clientLobbyList.sorted());
}
public void stopGame() {
GameState.setCurrentStage(GameStages.CANCELLED);
if (server != null) server.terminate();
if (socketThread != null) socketThread.setSocketToClose();
}
public Map<Integer, ClientYacht> getAllBoatsMap() {
return allBoatsMap;
}
}
+24 -469
View File
@@ -1,49 +1,24 @@
package seng302.visualiser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.util.Duration;
import seng302.gameServer.messages.RoundingSide;
import seng302.model.ClientYacht;
import seng302.model.GeoPoint;
import seng302.model.Limit;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner;
import seng302.model.mark.Mark;
import seng302.model.token.Token;
import seng302.utilities.GeoUtility;
import seng302.visualiser.fxObjects.assets_2D.AnnotationBox;
import seng302.visualiser.fxObjects.assets_2D.BoatObject;
import seng302.visualiser.fxObjects.assets_2D.CourseBoundary;
import seng302.visualiser.fxObjects.assets_2D.Gate;
import seng302.visualiser.fxObjects.assets_2D.MarkArrowFactory;
import seng302.visualiser.fxObjects.assets_2D.Marker;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
import seng302.visualiser.fxObjects.assets_3D.ModelType;
import seng302.visualiser.map.Boundary;
import seng302.visualiser.map.CanvasMap;
import seng302.visualiser.fxObjects.assets_2D.*;
import java.util.*;
/**
* Created by cir27 on 20/07/17.
@@ -51,8 +26,8 @@ import seng302.visualiser.map.CanvasMap;
public class GameView extends Pane {
private double bufferSize = 50;
private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias.
private double panelHeight = 960;
private double horizontalBuffer = 0;
private double canvasWidth = 1100;
private double canvasHeight = 920;
private boolean horizontalInversion = false;
@@ -61,186 +36,31 @@ public class GameView extends Pane {
private ScaleDirection scaleDirection;
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
private double referencePointX, referencePointY;
private double metersPerPixelX, metersPerPixelY;
final double SCALE_DELTA = 1.1;
private Text fpsDisplay = new Text();
private Polygon raceBorder = new CourseBoundary();
/* Note that if either of these is null then values for it have not been added and the other
should be used as the limits of the map. */
private List<Limit> borderPoints;
private Map<Mark, Marker> markerObjects;
private Map<Mark, Marker2D> markerObjects;
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
private ObservableList<Node> gameObjects;
private BoatObject selectedBoat = null;
private Group annotationsGroup = new Group();
private Group wakesGroup = new Group();
private Group boatObjectGroup = new Group();
private Group trails = new Group();
private Group markers = new Group();
private Group tokens = new Group();
private List<CompoundMark> course = new ArrayList<>();
private List<Node> mapTokens;
private ImageView mapImage = new ImageView();
private Camera camera;
//FRAME RATE
private AnimationTimer timer;
private int NUM_SAMPLES = 10;
private final long[] frameTimes = new long[NUM_SAMPLES];
private Double frameRate = 60.0;
private int frameTimeIndex = 0;
private boolean arrayFilled = false;
private ClientYacht playerYacht;
private double windDir = 0.0;
double scaleFactor = 1;
private void zoomOut() {
scaleFactor = 0.1;
if (this.getScaleX() > 0.5) {
this.setScaleX(this.getScaleX() - scaleFactor);
this.setScaleY(this.getScaleY() - scaleFactor);
}
}
private void zoomIn() {
scaleFactor = 0.10;
if (this.getScaleX() < 2.5) {
this.setScaleX(this.getScaleX() + scaleFactor);
this.setScaleY(this.getScaleY() + scaleFactor);
}
}
private enum ScaleDirection {
HORIZONTAL,
VERTICAL
}
private void trackBoat() {
if (selectedBoat != null) {
double x = selectedBoat.getBoatLayoutX();
double y = selectedBoat.getBoatLayoutY();
double displacementX = this.getWidth();
double displacementY = this.getHeight();
this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX());
this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY());
} else {
this.setLayoutX(0);
this.setLayoutY(0);
}
}
public GameView () {
gameObjects = this.getChildren();
// AmbientLight ambientLight = new AmbientLight(new Color(1,1,1,0.4));
// ambientLight.setOpacity(0.5);
// gameObjects.add(ambientLight);
// create image view for map, bind panel size to image
camera = new ParallelCamera();
camera.setTranslateZ(-500);
camera.setFarClip(Double.MAX_VALUE);
camera.setNearClip(0.1);
PointLight pl = new PointLight();
pl.setLightOn(true);
pl.layoutYProperty().bind(camera.layoutYProperty());
pl.layoutXProperty().bind(camera.layoutXProperty());
// gameObjects.add(camera);
this.sceneProperty().addListener((obs, oldValue, scene) -> {
if (scene != null) {
scene.setCamera(camera);
}
});
initializeTimer();
gameObjects.addAll(mapImage, raceBorder, markers, tokens, pl);
gameObjects.addAll(mapImage, raceBorder, markers, tokens);
}
private void initializeTimer() {
Arrays.fill(frameTimes, 1_000_000_000 / 60);
timer = new AnimationTimer() {
private long lastTime = 0;
private int FPSCount = 30;
private Double frameRate = 60.0;
private int index = 0;
private boolean arrayFilled = false;
private long sum = 1_000_000_000 / 3;
@Override
public void handle(long now) {
trackBoat();
if (lastTime == 0) {
lastTime = now;
} else {
if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized
long oldFrameTime = frameTimes[frameTimeIndex];
frameTimes[frameTimeIndex] = now;
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
if (frameTimeIndex == 0) {
arrayFilled = true;
}
long elapsedNanos;
if (arrayFilled) {
elapsedNanos = now - oldFrameTime;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
if (FPSCount-- == 0) {
FPSCount = 30;
drawFps(frameRate);
}
}
lastTime = now;
}
}
// boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
}
};
}
/**
* First find the top right and bottom left points' geo locations, then retrieve map from google
* to display on image view. - Haoming 22/5/2017
*/
private void drawGoogleMap() {
findMetersPerPixel();
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng());
// distance from top left extreme to panel origin (top left corner)
double distanceFromTopLeftToOrigin = Math.sqrt(
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
.pow(topLeftPoint.getY() * metersPerPixelY, 2));
// angle from top left extreme to panel origin
double bearingFromTopLeftToOrigin = Math
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
// the top left extreme
GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng());
GeoPoint originPos = GeoUtility
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
// distance from origin corner to bottom right corner of the panel
double distanceFromOriginToBottomRight = Math.sqrt(
Math.pow(panelHeight * metersPerPixelY, 2) + Math
.pow(panelWidth * metersPerPixelX, 2));
double bearingFromOriginToBottomRight = Math
.toDegrees(Math.atan2(panelWidth, -panelHeight));
GeoPoint bottomRightPos = GeoUtility
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
distanceFromOriginToBottomRight);
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
bottomRightPos.getLat(), originPos.getLng());
CanvasMap canvasMap = new CanvasMap(boundary);
mapImage.setImage(canvasMap.getMapImage());
mapImage.fitWidthProperty().bind(((AnchorPane) this.getParent()).heightProperty());
mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty());
}
// TODO: 16/08/17 Break up this function
/**
* Adds a course to the GameView. The view is scaled accordingly unless a border is set in which
* case the course is added relative ot the border.
@@ -303,10 +123,10 @@ public class GameView extends Pane {
rescaleRace(new ArrayList<>(markerObjects.keySet()));
}
//Move the Markers to initial position.
markerObjects.forEach(((mark, marker) -> {
markerObjects.forEach(((mark, marker2D) -> {
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
marker.setLayoutX(p2d.getX());
marker.setLayoutY(p2d.getY());
marker2D.setLayoutX(p2d.getX());
marker2D.setLayoutY(p2d.getY());
}));
Platform.runLater(() -> {
markers.getChildren().clear();
@@ -396,9 +216,9 @@ public class GameView extends Pane {
* @param colour The desired colour of the mark
*/
private void makeAndBindMarker(Mark observableMark, Paint colour) {
Marker marker = new Marker(colour);
Marker2D marker2D = new Marker2D(colour);
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
markerObjects.put(observableMark, marker);
markerObjects.put(observableMark, marker2D);
observableMark.addPositionListener((mark, lat, lon) -> {
Point2D p2d = findScaledXY(lat, lon);
markerObjects.get(mark).setLayoutX(p2d.getX());
@@ -414,7 +234,7 @@ public class GameView extends Pane {
* @param colour The desired colour of the gate.
* @return the new gate.
*/
private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) {
private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) {
Gate gate = new Gate(colour);
gate.startXProperty().bind(
m1.layoutXProperty()
@@ -442,6 +262,9 @@ public class GameView extends Pane {
borderPoints = border;
rescaleRace(new ArrayList<>(borderPoints));
}
rescaleRace(new ArrayList<>(border));
List<Double> boundaryPoints = new ArrayList<>();
for (Limit limit : border) {
Point2D location = findScaledXY(limit.getLat(), limit.getLng());
@@ -456,139 +279,11 @@ public class GameView extends Pane {
*
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
*/
private void rescaleRace(List<GeoPoint> limitingCoordinates) {
public void rescaleRace(List<GeoPoint> limitingCoordinates) {
//Check is called once to avoid unnecessarily change the course limits once the race is running
findMinMaxPoint(limitingCoordinates);
double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon);
// drawGoogleMap();
}
/**
* Replaces all tokens in the course with those passed in
*
* @param newTokens the tokens to be put on the course.
*/
public void updateTokens(List<Token> newTokens) {
mapTokens = new ArrayList<>();
for (Token token : newTokens) {
Point2D location = findScaledXY(token.getLat(), token.getLng());
Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets();
tokenObject.setLayoutX(location.getX());
tokenObject.setLayoutY(location.getY());
mapTokens.add(tokenObject);
}
Platform.runLater(() -> {
tokens.getChildren().clear();
tokens.getChildren().addAll(mapTokens);
});
}
// TODO: 16/08/17 initialize zooming internal to GameView only
/**
* Enables zoom. Has to be called after this is added to a scene.
*/
public void enableZoom () {
if (this.getScene() != null) {
this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
if (event.getCode() == KeyCode.Z) {
zoomIn();
} else if (event.getCode() == KeyCode.X) {
zoomOut();
}
});
}
}
private void setSelectedBoat(BoatObject bo, Boolean isSelected) {
if (this.selectedBoat == bo && !isSelected) {
this.selectedBoat = null;
boatObjects.forEach((boat, group) ->
group.setIsSelected(false)
);
} else if (isSelected) {
this.selectedBoat = bo;
for (BoatObject group : boatObjects.values()) {
if (group != bo) {
group.setIsSelected(false);
}
}
}
}
/**
* Draws all the boats.
* @param yachts The yachts to set in the race
*/
public void setBoats(List<ClientYacht> yachts) {
BoatObject newBoat;
final List<Group> wakes = new ArrayList<>();
for (ClientYacht clientYacht : yachts) {
Color colour = clientYacht.getColour();
newBoat = new BoatObject();
newBoat.addSelectedBoatListener(this::setSelectedBoat);
newBoat.setFill(colour);
boatObjects.put(clientYacht, newBoat);
createAndBindAnnotationBox(clientYacht, colour);
// wakesGroup.getChildren().add(newBoat.getWake());
wakes.add(newBoat.getWake());
boatObjectGroup.getChildren().add(newBoat);
trails.getChildren().add(newBoat.getTrail());
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
bo.setTrajectory(
heading,
velocity,
metersPerPixelX,
metersPerPixelY);
});
}
annotationsGroup.getChildren().addAll(annotations.values());
Platform.runLater(() -> {
gameObjects.addAll(trails);
gameObjects.addAll(wakes);
gameObjects.addAll(annotationsGroup);
gameObjects.addAll(boatObjectGroup);
});
}
private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) {
AnnotationBox newAnnotation = new AnnotationBox();
newAnnotation.setFill(colour);
newAnnotation.addAnnotation(
"name", "Player: " + clientYacht.getShortName()
);
// newAnnotation.addAnnotation(
// "velocity",
// yacht.getVelocityProperty(),
// (velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue())
// );
// newAnnotation.addAnnotation(
// "nextMark",
// yacht.timeTillNextProperty(),
// (time) -> {
// DateFormat format = new SimpleDateFormat("mm:ss");
// return format.format(time);
// }
// );
// newAnnotation.addAnnotation(
// "lastMark",
// yacht.timeTillNextProperty(),
// (time) -> {
// DateFormat format = new SimpleDateFormat("mm:ss");
// return format.format(time);
// }
// );
annotations.put(clientYacht, newAnnotation);
}
private void drawFps(Double fps) {
Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps))));
}
/**
@@ -651,6 +346,7 @@ public class GameView extends Pane {
referencePointX +=
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
/ 2;
referencePointX += horizontalBuffer;
}
if (horizontalInversion) {
referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize);
@@ -692,10 +388,6 @@ public class GameView extends Pane {
return horiDistance;
}
private Point2D findScaledXY(GeoPoint unscaled) {
return findScaledXY(unscaled.getLat(), unscaled.getLng());
}
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
double distanceFromReference;
double angleFromReference;
@@ -738,150 +430,13 @@ public class GameView extends Pane {
return new Point2D(xAxisLocation, yAxisLocation);
}
/**
* Find the number of meters per pixel.
*/
private void findMetersPerPixel() {
Point2D p1, p2;
GeoPoint g1, g2;
double theta, distance, dx, dy, dHorizontal, dVertical;
g1 = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng());
g2 = new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng());
p1 = findScaledXY(new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()));
p2 = findScaledXY(new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng()));
theta = GeoUtility.getBearingRad(g1, g2);
distance = GeoUtility.getDistance(g1, g2);
dHorizontal = Math.abs(Math.sin(theta) * distance);
dVertical = Math.abs(Math.cos(theta) * distance);
dx = Math.abs(p1.getX() - p2.getX());
dy = Math.abs(p1.getY() - p2.getY());
metersPerPixelX = dHorizontal / dx;
metersPerPixelY = dVertical / dy;
public void setSize(Double width, Double height){
this.canvasWidth = width;
this.canvasHeight = height;
}
public void setAnnotationVisibilities(boolean teamName, boolean velocity, boolean estTime,
boolean legTime, boolean trail, boolean wake) {
for (BoatObject boatObject : boatObjects.values()) {
boatObject.setVisibility(teamName, velocity, estTime, legTime, trail, wake);
}
for (AnnotationBox ag : annotations.values()) {
ag.setAnnotationVisibility("name", teamName);
ag.setAnnotationVisibility("velocity", velocity);
ag.setAnnotationVisibility("nextMark", estTime);
ag.setAnnotationVisibility("lastMark", legTime);
}
}
public void setFPSVisibility(boolean visibility) {
fpsDisplay.setVisible(visibility);
}
public void selectBoat(ClientYacht selectedClientYacht) {
boatObjects.forEach((boat, group) ->
group.setIsSelected(boat == selectedClientYacht)
);
}
public void pauseRace() {
timer.stop();
}
public void setWindDir(double windDir) {
this.windDir = windDir;
}
public void startRace() {
timer.start();
}
public ClientYacht getPlayerYacht() {
return playerYacht;
}
public void setBoatAsPlayer (ClientYacht playerYacht) {
this.playerYacht = playerYacht;
playerYacht.toggleSail();
boatObjects.get(playerYacht).setAsPlayer();
CompoundMark currentMark = course.get(playerYacht.getLegNumber());
for (Mark mark : currentMark.getMarks()) {
markerObjects.get(mark).showNextExitArrow();
}
annotations.get(playerYacht).addAnnotation(
"velocity",
playerYacht.getVelocityProperty(),
(velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue())
);
Platform.runLater(() -> {
boatObjectGroup.getChildren().remove(boatObjects.get(playerYacht));
gameObjects.add(boatObjects.get(playerYacht));
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
gameObjects.add(annotations.get(playerYacht));
});
playerYacht.addMarkRoundingListener(this::updateMarkArrows);
}
private void updateMarkArrows (ClientYacht yacht, CompoundMark compoundMark, int legNumber) {
//Only show arrows for this and next leg.
// System.out.println(markerObjects);
if (compoundMark != null) {
for (Mark mark : compoundMark.getMarks()) {
// System.out.println("markerObjects.get(mark) = " + markerObjects.get(mark));
markerObjects.get(mark).showNextExitArrow();
}
}
CompoundMark nextMark = null;
if (legNumber < course.size() - 1) {
nextMark = course.get(legNumber);
for (Mark mark : nextMark.getMarks()) {
markerObjects.get(mark).showNextEnterArrow();
}
}
if (legNumber - 2 >= 0) {
CompoundMark lastMark = course.get(Math.max(0, legNumber - 2));
if (lastMark != nextMark) {
for (Mark mark : lastMark.getMarks()) {
markerObjects.get(mark).hideAllArrows();
}
}
}
}
/**
* Given yacht geopoint by race view controller, drawCollision will calculate canvas X and Y and
* display a flashing red circle on collision point.
*
* @param collisionPoint yacht collision point
*/
public void drawCollision(GeoPoint collisionPoint) {
Point2D point = findScaledXY(collisionPoint);
double circleRadius = 0.0;
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.RED);
circle.setStrokeWidth(3);
Timeline timeline = new Timeline();
timeline.setCycleCount(1);
KeyFrame keyframe1 = new KeyFrame(Duration.ZERO,
new KeyValue(circle.radiusProperty(), 0),
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
KeyFrame keyFrame2 = new KeyFrame(new Duration(1000),
new KeyValue(circle.radiusProperty(), 50),
new KeyValue(circle.strokeProperty(), Color.RED));
KeyFrame keyFrame3 = new KeyFrame(new Duration(1500),
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3);
Platform.runLater(() -> gameObjects.add(circle));
timeline.setOnFinished(event -> Platform.runLater(() -> gameObjects.remove(circle)));
timeline.play();
}
public void setFrameRateFXText(Text fpsDisplay) {
this.fpsDisplay = null;
this.fpsDisplay = fpsDisplay;
public void setHorizontalBuffer(Double buff){
this.horizontalBuffer = buff;
}
}
@@ -0,0 +1,113 @@
package seng302.visualiser;
import seng302.gameServer.ServerAdvertiser;
import seng302.gameServer.ServerDescription;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import static seng302.gameServer.ServerAdvertiser.getLocalHostIp;
/**
* Listens for servers on the local network
*/
public class ServerListener{
private static ServerListener instance;
private ServerListenerDelegate delegate;
private JmDNS jmdns = null;
GameServeMonitor listener;
private class GameServeMonitor implements ServiceListener {
private Set<ServerDescription> servers;
GameServeMonitor(){
servers = new HashSet<>();
}
/**
* A Service has been detected but not resolved
* @param event The ServiceEvent
*/
@Override
public void serviceAdded(ServiceEvent event) {
}
/**
* A Service has been removed / unregistered
* @param event The ServiceEvent
*/
@Override
public void serviceRemoved(ServiceEvent event) {
String serverName = event.getInfo().getName();
ServerDescription toRemove = null;
for (ServerDescription server : servers){
if (server.getName().equals(serverName)){
toRemove = server;
}
}
if (toRemove != null){
servers.remove(toRemove);
}
delegate.serverRemoved(new ArrayList<ServerDescription>(servers));
// Get all other servers with the same name to respond if they are up
jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverName);
}
/**
* A Service has been added and resolved
* @param event The ServiceEvent
*/
@Override
public void serviceResolved(ServiceEvent event) {
String address = event.getInfo().getServer();
Integer portNum = event.getInfo().getPort();
String serverName = event.getInfo().getName();
String mapName = event.getInfo().getPropertyString("map");
Integer capacity = Integer.parseInt(event.getInfo().getPropertyString("capacity"));
Integer numPlayers = Integer.parseInt(event.getInfo().getPropertyString("players"));
ServerDescription serverDescription = new ServerDescription(serverName, mapName, numPlayers, capacity, address, portNum);
servers.remove(serverDescription);
servers.add(serverDescription);
delegate.serverDetected(serverDescription, new ArrayList<>(servers));
}
}
private ServerListener() throws IOException {
jmdns = JmDNS.create(InetAddress.getByName(getLocalHostIp()));
listener = new GameServeMonitor();
jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener);
}
public static ServerListener getInstance() throws IOException {
if (instance == null){
instance = new ServerListener();
}
return instance;
}
/**
* Set the delegate to handle events
* @param delegate .
*/
public void setDelegate(ServerListenerDelegate delegate){
this.delegate = delegate;
}
}
@@ -0,0 +1,10 @@
package seng302.visualiser;
import seng302.gameServer.ServerDescription;
import java.util.List;
public interface ServerListenerDelegate {
void serverRemoved(List<ServerDescription> currentServers);
void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers);
}
@@ -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();
}
}
@@ -0,0 +1,76 @@
package seng302.visualiser.fxObjects;
import java.util.Arrays;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.paint.Paint;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
/**
* Extension of a ScrollPane that contains a TextFlow. Has an addMessage() function to parse and
* display chatter text.
*/
public class ChatHistory extends ScrollPane {
private TextFlow textFlow = new TextFlow();
public ChatHistory() {
this.setContent(textFlow);
this.setFitToWidth(true);
this.setFitToHeight(true);
this.setMaxHeight(Double.MAX_VALUE);
this.setMaxWidth(Double.MAX_VALUE);
this.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
this.setHbarPolicy(ScrollBarPolicy.NEVER);
this.lookup(".scroll-pane").setStyle("-fx-background: rgba(255, 255, 255, 0.1); -fx-background-color: rgba(255, 255, 255, 0.1);");
this.textFlow.setStyle(
"-fx-background: rgba(255, 255, 255, 0.1); -fx-background-color: rgba(255, 255, 255, 0.1); -fx-padding: 10px"
);
//This makes the window auto scroll.
textFlow.getChildren().addListener((ListChangeListener<Node>) c ->
this.setVvalue(1.0)
);
//This just makes it so that the ChatHistory is on focus it passes it off to the parent.
this.parentProperty().addListener((obs, old, parent) ->
this.focusedProperty().addListener((obsVal, oldVal, onFocus) -> {
if (onFocus) {
parent.requestFocus();
}
})
);
}
/**
* Adds a message to chat history. Messages should be either of the form:
* "[HH:MM:ss] player_name: message_text" or
* "SERVER: message_text"
* @param colour The colour of the user sending the message
* @param Text The chatter text message to be displayed
*/
public void addMessage (Paint colour, String Text) {
String[] words = Text.split(":");
if (words[0].trim().equals("SERVER")) {
Text text = new Text(Text + "\n");
text.setStyle("-fx-font-weight: bolder");
textFlow.getChildren().add(text);
} else {
Text timePlayer = new Text(
String.join(":", Arrays.copyOfRange(words, 0, 3)) + ":"
);
timePlayer.setStyle("-fx-font-weight: bold");
timePlayer.setFill(colour);
Text message = new Text(
String.join(":", Arrays.copyOfRange(words, 3, words.length)) + "\n"
);
message.wrappingWidthProperty().bind(this.widthProperty());
textFlow.getChildren().addAll(timePlayer, message);
}
}
}
@@ -1,7 +1,5 @@
package seng302.visualiser.fxObjects.assets_2D;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleDoubleProperty;
@@ -25,6 +23,9 @@ import seng302.visualiser.fxObjects.assets_3D.BoatModel;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
import seng302.visualiser.fxObjects.assets_3D.ModelType;
import java.util.ArrayList;
import java.util.List;
/**
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
* dimensional boat. It contains a single polygon for the boat, a group of lines to show it's path,
@@ -189,6 +190,7 @@ public class BoatObject extends Group {
* @param rotation The rotation by which the boat moves
* @param velocity The velocity the boat is moving
* @param sailIn Boolean to toggle sail state.
* @param windDir .
*/
public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) {
Double dx = Math.abs(boatAssets.getAssets().getLayoutX() - x);
@@ -0,0 +1,100 @@
package seng302.visualiser.fxObjects.assets_2D;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
/**
* Visual object for a mark. Contains a coloured circle and any specified arrows.
*/
public class Marker2D extends Group {
private Circle mark = new Circle();
private Paint colour = Color.BLACK;
private List<Group> enterArrows = new ArrayList<>();
private List<Group> exitArrows = new ArrayList<>();
private int enterArrowIndex = 0;
private int exitArrowIndex = 0;
/**
* Creates a new Marker containing only a circle. The default colour is black.
*/
public Marker2D() {
mark.setRadius(5);
mark.setCenterX(0);
mark.setCenterY(0);
Platform.runLater(() -> this.getChildren()
.addAll(mark, new Group())); //Empty group placeholder or arrows.
}
/**
* Creates a new Marker containing only a circle of the given colour.
*
* @param colour the desired colour for the marker.
*/
public Marker2D(Paint colour) {
this();
this.colour = colour;
mark.setFill(colour);
}
/**
* Adds an exit and entry arrow pair to the mark. Arrows are hidden and shown in the order they
* are created by calling showNextEnterArrow() or showNextExitArrow()
*
* @param roundingSide the side the marker will be from the perspective of the arrow.
* @param entryAngle The angle the arrow will point towards a marker
* @param exitAngle The angle the arrow wil point from the marker.
*/
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
double exitAngle) {
//Change Color.GRAY to this.colour to revert all gray arrows.
enterArrows.add(
MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, Color.GRAY)
);
exitArrows.add(
MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, Color.GRAY)
);
}
/**
* Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows
* become hidden.
*/
public void showNextEnterArrow() {
showArrow(enterArrows, enterArrowIndex);
enterArrowIndex++;
}
/**
* Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become
* hidden.
*/
public void showNextExitArrow() {
showArrow(exitArrows, exitArrowIndex);
exitArrowIndex++;
}
private void showArrow(List<Group> arrowList, int arrowListIndex) {
if (arrowListIndex < arrowList.size()) {
if (arrowListIndex == 1) {
;
}
Platform.runLater(() -> {
this.getChildren().remove(1);
this.getChildren().add(arrowList.get(arrowListIndex));
});
}
}
/**
* Hides all arrows.
*/
public void hideAllArrows() {
Platform.runLater(() -> this.getChildren().setAll(mark, new Group()));
}
}
@@ -1,4 +1,4 @@
package seng302.visualiser.fxObjects.assets_2D;
package seng302.visualiser.fxObjects.assets_3D;
import java.util.ArrayList;
import java.util.List;
@@ -6,15 +6,14 @@ import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Scale;
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
import seng302.visualiser.fxObjects.assets_3D.ModelType;
import seng302.visualiser.fxObjects.assets_2D.MarkArrowFactory;
import seng302.visualiser.fxObjects.assets_2D.MarkArrowFactory.RoundingSide;
/**
* Visual object for a mark. Contains a coloured circle and any specified arrows.
*/
public class Marker extends Group {
public class Marker3D extends Group {
private Group mark = ModelFactory.importModel(ModelType.PLAIN_MARKER).getAssets();
private Paint colour = Color.BLACK;
@@ -26,7 +25,7 @@ public class Marker extends Group {
/**
* Creates a new Marker containing only a circle. The default colour is black.
*/
public Marker() {
public Marker3D() {
// mark.setRadius(5);
// mark.setCenterX(0);
// mark.setCenterY(0);
@@ -40,7 +39,7 @@ public class Marker extends Group {
* Creates a new Marker containing only a circle of the given colour.
* @param colour the desired colour for the marker.
*/
public Marker(Paint colour) {
public Marker3D(Paint colour) {
this();
this.colour = colour;
// mark.setFill(colour);
@@ -53,7 +52,7 @@ public class Marker extends Group {
* @param entryAngle The angle the arrow will point towards a marker
* @param exitAngle The angle the arrow wil point from the marker.
*/
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
public void addArrows(RoundingSide roundingSide, double entryAngle,
double exitAngle) {
//Change Color.GRAY to this.colour to revert all gray arrows.
enterArrows.add(
@@ -4,7 +4,7 @@ import javafx.animation.AnimationTimer;
import javafx.scene.Group;
/**
* Created by CJIRWIN on 7/09/2017.
* Class for generic imported 3D model. Animation terminates on if removed from scene.
*/
public class Model {
@@ -16,10 +16,16 @@ public class Model {
this.animationTimer = animation;
if (animation != null) {
animation.start();
assets.sceneProperty().addListener((obs, oldVal, newVal) -> {
if (newVal == null) {
animationTimer.stop();
animationTimer = null;
}
});
}
}
void setAnimation(AnimationTimer animation) {
public void setAnimation(AnimationTimer animation) {
animationTimer = animation;
if (animation != null) {
animation.start();
@@ -14,6 +14,8 @@ import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
/**
* Factory class for creating 3D models of boats.
*/
@@ -21,13 +23,31 @@ public class ModelFactory {
public static BoatModel boatIconView(BoatMeshType boatType, Color primaryColour) {
Group boatAssets = getUnmodifiedBoatModel(boatType, primaryColour);
final Rotate animationRotate = new Rotate(0, new Point3D(0,0,1));
boatAssets.getTransforms().addAll(
new Scale(20, 20, 20),
new Rotate(90, new Point3D(0,0,1)),
new Rotate(90, new Point3D(0, 1, 0))
new Scale(3.3, 3.3, 3.3),
new Rotate(-70, new Point3D(1,0,0)),
new Translate(13,50, 0),
animationRotate
);
boatAssets.getChildren().add(new AmbientLight(new Color(1, 1, 1, 0.01)));
return new BoatModel(boatAssets, null, boatType);
boatAssets.getTransforms().add(animationRotate);
BoatModel bo = new BoatModel(boatAssets, null, boatType);
bo.rotateSail(45);
bo.setAnimation(new AnimationTimer() {
double boatAngle = 0;
Rotate rotate = animationRotate;
@Override
public void handle(long now) {
boatAngle += 0.5;
rotate.setAngle(boatAngle);
}
});
boatAssets.getChildren().addAll(
new AmbientLight()
);
return bo;
}
public static BoatModel boatRotatingView(BoatMeshType boatType, Color primaryColour) {
@@ -35,19 +55,21 @@ public class ModelFactory {
boatAssets.getTransforms().addAll(
new Scale(40, 40, 40),
new Rotate(90, new Point3D(0,0,1)),
new Rotate(90, new Point3D(0, 1, 0)),
new Rotate(0, new Point3D(1,1,1))
new Rotate(90, new Point3D(0, 1, 0))
);
// TODO: 7/09/17 This seems like it will never be garbage claimed. Might have to call BoatModel.stopAnimation();
final Rotate animationRotate = new Rotate(0, new Point3D(1,1,1));
boatAssets.getTransforms().add(animationRotate);
return new BoatModel(boatAssets, new AnimationTimer() {
private double rotation = 0;
private final Group group = boatAssets;
private Rotate rotate = animationRotate;
@Override
public void handle(long now) {
rotation += 0.5;
((Rotate) group.getTransforms().get(3)).setAngle(rotation);
rotate.setAngle(rotation);
}
}, boatType);
}
@@ -0,0 +1,36 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package seng302.visualiser.validators;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.validation.base.ValidatorBase;
import javafx.beans.DefaultProperty;
import javafx.scene.control.TextInputControl;
@DefaultProperty("icon")
public class FieldLengthValidator extends ValidatorBase {
Integer maxLength;
public FieldLengthValidator(Integer length) {
maxLength = length;
}
protected void eval() {
if(this.srcControl.get() instanceof TextInputControl) {
this.evalTextInputField();
}
}
private void evalTextInputField() {
TextInputControl textField = (TextInputControl)this.srcControl.get();
if(textField.getLength() > maxLength) {
this.hasErrors.set(true);
} else {
this.hasErrors.set(false);
}
}
}
@@ -0,0 +1,32 @@
package seng302.visualiser.validators;
import com.jfoenix.validation.base.ValidatorBase;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javafx.beans.DefaultProperty;
import javafx.scene.control.TextInputControl;
@DefaultProperty("icon")
public class HostNameFieldValidator extends ValidatorBase {
public HostNameFieldValidator() {
}
protected void eval() {
if(this.srcControl.get() instanceof TextInputControl) {
this.evalTextInputField();
}
}
protected void evalTextInputField() {
TextInputControl textField = (TextInputControl)this.srcControl.get();
try{
InetAddress.getByName(textField.getText());
this.hasErrors.set(false);
} catch (UnknownHostException e) {
this.hasErrors.set(true);
}
}
}
@@ -0,0 +1,40 @@
package seng302.visualiser.validators;
import com.jfoenix.validation.base.ValidatorBase;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javafx.beans.DefaultProperty;
import javafx.scene.control.TextInputControl;
@DefaultProperty("icon")
public class NumberRangeValidator extends ValidatorBase {
Integer lowerLimit;
Integer upperLimit;
public NumberRangeValidator(Integer lower, Integer upper) {
lowerLimit = lower;
upperLimit = upper;
}
protected void eval() {
if(this.srcControl.get() instanceof TextInputControl) {
this.evalTextInputField();
}
}
protected void evalTextInputField() {
TextInputControl textField = (TextInputControl)this.srcControl.get();
try {
Integer portNum = Integer.parseInt(textField.getText());
if (lowerLimit <= portNum && portNum <= upperLimit) {
this.hasErrors.set(false);
} else {
this.hasErrors.set(true);
}
} catch (NumberFormatException e) {
this.hasErrors.set(true);
}
}
}
@@ -0,0 +1,21 @@
package seng302.visualiser.validators;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.base.ValidatorBase;
public class ValidationTools {
/**
*
* @return
*/
public static Boolean validateTextField(JFXTextField textField) {
textField.validate();
for (ValidatorBase validator : textField.getValidators()) {
if (validator.getHasErrors()) {
return false;
}
}
return true;
}
}