Merge remote-tracking branch 'origin/develop' into 1273_Skybox

# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/gameServer/MessageFactory.java
#	src/main/java/seng302/gameServer/ServerToClientThread.java
#	src/main/java/seng302/model/ClientYacht.java
#	src/main/java/seng302/model/mark/MarkOrder.java
#	src/main/java/seng302/visualiser/GameClient.java
#	src/main/java/seng302/visualiser/GameView3D.java
#	src/main/java/seng302/visualiser/controllers/ServerListController.java
#	src/main/java/seng302/visualiser/controllers/dialogs/ServerCreationController.java
#	src/main/resources/icons/bumperIcon.png
#	src/main/resources/icons/handlingIcon.png
#	src/main/resources/icons/velocity.png
#	src/main/resources/icons/windWalkerIcon.png
#	src/main/resources/views/RaceView.fxml
#	src/main/resources/views/dialogs/ServerCreationDialog.fxml
This commit is contained in:
Michael Rausch
2017-09-27 14:23:38 +13:00
41 changed files with 1554 additions and 587 deletions
+281 -116
View File
@@ -1,14 +1,7 @@
package seng302.gameServer; package seng302.gameServer;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,8 +13,6 @@ import seng302.gameServer.messages.MarkRoundingMessage;
import seng302.gameServer.messages.MarkType; import seng302.gameServer.messages.MarkType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RoundingBoatStatus; import seng302.gameServer.messages.RoundingBoatStatus;
import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.gameServer.messages.YachtEventType;
import seng302.model.GeoPoint; import seng302.model.GeoPoint;
import seng302.model.Limit; import seng302.model.Limit;
import seng302.model.Player; import seng302.model.Player;
@@ -34,6 +25,8 @@ import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.Token; import seng302.model.token.Token;
import seng302.model.token.TokenType; import seng302.model.token.TokenType;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.utilities.RandomSpawn;
import seng302.utilities.XMLParser;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
/** /**
@@ -51,42 +44,55 @@ public class GameState implements Runnable {
private static Logger logger = LoggerFactory.getLogger(GameState.class); private static Logger logger = LoggerFactory.getLogger(GameState.class);
private static final Integer STATE_UPDATES_PER_SECOND = 60;
//Scheduling constants
static final int WARNING_TIME = 10 * -1000; static final int WARNING_TIME = 10 * -1000;
static final int PREPATORY_TIME = 5 * -1000; static final int PREPATORY_TIME = 5 * -1000;
private static final int TIME_TILL_START = 10 * 1000; private static final int TIME_TILL_START = 10 * 1000;
private static final Long POWERUP_TIMEOUT_MS = 10_000L; //Wind Constants
private static final int MAX_WIND_SPEED = 12000;
private static final int MIN_WIND_SPEED = 8000;
private static final Integer STATE_UPDATES_PER_SECOND = 60; //Rounding Constants
private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further private static final Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
//Collision constants
private static final Double MARK_COLLISION_DISTANCE = 15d; private static final Double MARK_COLLISION_DISTANCE = 15d;
public static final Double YACHT_COLLISION_DISTANCE = 25.0; public static final Double YACHT_COLLISION_DISTANCE = 25.0;
private static final Double BOUNCE_DISTANCE_MARK = 20.0; private static final Double BOUNCE_DISTANCE_MARK = 20.0;
public static final Double BOUNCE_DISTANCE_YACHT = 30.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
private static final Double COLLISION_VELOCITY_PENALTY = 0.3; private static final Double COLLISION_VELOCITY_PENALTY = 0.3;
private static final Integer VELOCITY_BOOST_MULTIPLIER = 2;
//Powerup Constants
public static final Double VELOCITY_BOOST_MULTIPLIER = 2d;
public static final Integer HANDLING_BOOST_MULTIPLIER = 2;
private static final Double BAD_RANDOM_SPEED_PENALTY = 0.3;
public static final Long BUMPER_DISABLE_TIME = 5_000L;
private static final Long TOKEN_SPAWN_TIME = 30_000L;
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
private static Double windSpeed; private static Double windSpeed;
private static Double speedMultiplier = 1d; private static Double serverSpeedMultiplier;
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat. private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
private static Boolean playerHasLeftFlag; private static Boolean playerHasLeftFlag;
private static String hostIpAddress;
private static List<Player> players; private static List<Player> players;
private static Map<Integer, ServerYacht> yachts; private static Map<Integer, ServerYacht> yachts;
private static Boolean isRaceStarted; private static Boolean isRaceStarted;
private static GameStages currentStage; private static GameStages currentStage;
private static MarkOrder markOrder; private static MarkOrder markOrder;
private static long startTime; private static long startTime;
private static Set<Mark> marks; private static List<Mark> marks;
private static List<Limit> courseLimit; private static List<Limit> courseLimit;
private static Integer maxPlayers = 8; private static Integer maxPlayers = 8;
private static List<Token> allTokens;
private static List<Token> tokensInPlay; private static List<Token> tokensInPlay;
private static RandomSpawn randomSpawn;
private static List<NewMessageListener> newMessageListeners; private static List<NewMessageListener> newMessageListeners;
@@ -101,14 +107,16 @@ public class GameState implements Runnable {
players = new ArrayList<>(); players = new ArrayList<>();
customizationFlag = false; customizationFlag = false;
playerHasLeftFlag = false; playerHasLeftFlag = false;
speedMultiplier = 1.0; serverSpeedMultiplier = 1.0;
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false; isRaceStarted = false;
//set this when game stage changes to prerace
previousUpdateTime = System.currentTimeMillis(); previousUpdateTime = System.currentTimeMillis();
newMessageListeners = new ArrayList<>(); newMessageListeners = new ArrayList<>();
allTokens = makeTokens(); marks = new MarkOrder().getAllMarks();
randomSpawn = new RandomSpawn(markOrder.getOrderedUniqueCompoundMarks());
resetStartTime(); resetStartTime();
//setCourseLimit("/server_config/race.xml");
new Thread(this, "GameState").start(); //Run the auto updates on the game state new Thread(this, "GameState").start(); //Run the auto updates on the game state
} }
@@ -120,24 +128,6 @@ public class GameState implements Runnable {
courseLimit = raceXMLData.getCourseLimit(); courseLimit = raceXMLData.getCourseLimit();
} }
/**
* Make a pre defined set of tokensInPlay. //TODO wmu16 - Should read from some file for each
* race ideally
*
* @return A list of possible tokensInPlay for this race
*/
private ArrayList<Token> makeTokens() {
Token token1 = new Token(TokenType.BOOST, 57.66946, 11.83154);
Token token2 = new Token(TokenType.BOOST, 57.66877, 11.83382);
Token token3 = new Token(TokenType.BOOST, 57.66914, 11.83965);
Token token4 = new Token(TokenType.BOOST, 57.66684, 11.83214);
return new ArrayList<>(Arrays.asList(token1, token2, token3, token4));
}
public static Set<Mark> getMarks() {
return Collections.unmodifiableSet(marks);
}
public static List<Player> getPlayers() { public static List<Player> getPlayers() {
return players; return players;
} }
@@ -146,6 +136,10 @@ public class GameState implements Runnable {
return tokensInPlay; return tokensInPlay;
} }
public static Set<Mark> getMarks() {
return Collections.unmodifiableSet(marks);
}
public static void addPlayer(Player player) { public static void addPlayer(Player player) {
players.add(player); players.add(player);
String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName() String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName()
@@ -238,12 +232,75 @@ public class GameState implements Runnable {
} catch (InterruptedException e) { } catch (InterruptedException e) {
System.out.println("[GameState] interrupted exception"); System.out.println("[GameState] interrupted exception");
} }
if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) { if (currentStage == GameStages.PRE_RACE) {
update();
if (System.currentTimeMillis() > startTime) {
startSpawningTokens();
startUpdatingWind();
GameState.setCurrentStage(GameStages.RACING);
}
}
if (currentStage == GameStages.RACING) {
update(); update();
} }
} }
} }
/**
* Start spawning coins every 60s after the first minute
*/
private void startSpawningTokens() {
Timer timer = new Timer("Token Spawning Timer");
timer.schedule(new TimerTask() {
@Override
public void run() {
spawnNewToken();
notifyMessageListeners(MessageFactory.getRaceXML());
}
}, 0, TOKEN_SPAWN_TIME);
}
// TODO: 29/08/17 wmu16 - This sort of update should be in game state
private static void startUpdatingWind() {
Timer timer = new Timer("Wind Updating Timer");
timer.schedule(new TimerTask() {
@Override
public void run() {
updateWind();
}
}, 0, 500);
}
private static void updateWind() {
Integer direction = GameState.getWindDirection().intValue();
Integer windSpeed = GameState.getWindSpeedMMS().intValue();
Random random = new Random();
if (Math.floorMod(random.nextInt(), 2) == 0) {
direction += random.nextInt(4);
windSpeed += random.nextInt(20) + 459;
} else {
direction -= random.nextInt(4);
windSpeed -= random.nextInt(20) + 459;
}
direction = Math.floorMod(direction, 360);
if (windSpeed > MAX_WIND_SPEED) {
windSpeed -= random.nextInt(500);
}
if (windSpeed <= MIN_WIND_SPEED) {
windSpeed += random.nextInt(500);
}
GameState.setWindSpeed(Double.valueOf(windSpeed));
GameState.setWindDirection(direction.doubleValue());
}
public static void updateBoat(Integer sourceId, BoatAction actionType) { public static void updateBoat(Integer sourceId, BoatAction actionType) {
ServerYacht playerYacht = yachts.get(sourceId); ServerYacht playerYacht = yachts.get(sourceId);
switch (actionType) { switch (actionType) {
@@ -278,14 +335,11 @@ public class GameState implements Runnable {
* Randomly select a subset of tokensInPlay from a pre defined superset * Randomly select a subset of tokensInPlay from a pre defined superset
* Broadasts a new race status message to show this update * Broadasts a new race status message to show this update
*/ */
public static void spawnNewToken() { private void spawnNewToken() {
Random random = new Random();
tokensInPlay.clear(); tokensInPlay.clear();
Token token = randomSpawn.getRandomToken();
//Get a random token location with random type // token.assignType(TokenType.RANDOM);
Token token = allTokens.get(random.nextInt(allTokens.size())); logger.debug("Spawned token of type " + token.getTokenType());
token.assignRandomType();
tokensInPlay.add(token); tokensInPlay.add(token);
} }
@@ -303,14 +357,12 @@ public class GameState implements Runnable {
Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0; Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0;
previousUpdateTime = System.currentTimeMillis(); previousUpdateTime = System.currentTimeMillis();
if (System.currentTimeMillis() > startTime) {
GameState.setCurrentStage(GameStages.RACING);
}
for (ServerYacht yacht : yachts.values()) { for (ServerYacht yacht : yachts.values()) {
updateVelocity(yacht); updateVelocity(yacht);
checkPowerUpTimeout(yacht);
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
preformTokenUpdates(yacht); //This update must be done before collision. Sorta hacky
checkCollision(yacht); checkCollision(yacht);
if (yacht.getBoatStatus() != BoatStatus.FINISHED) { if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
checkForLegProgression(yacht); checkForLegProgression(yacht);
@@ -324,17 +376,136 @@ public class GameState implements Runnable {
} }
private void checkPowerUpTimeout(ServerYacht yacht) { /**
if (yacht.getPowerUp() != null) { * All token functionality entry points is taken care of here. So can be disabled and enabled
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > POWERUP_TIMEOUT_MS) { * easily
yacht.powerDown(); *
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired"); * @param yacht The yacht to perform token checks on
logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); */
private void preformTokenUpdates(ServerYacht yacht) {
Token collidedToken = checkTokenPickUp(yacht);
if (collidedToken != null) {
tokensInPlay.remove(collidedToken);
powerUpYacht(yacht, collidedToken);
}
checkPowerUpTimeout(yacht);
TokenType powerUp = yacht.getPowerUp();
if (powerUp != null) {
switch (powerUp) {
case WIND_WALKER:
windWalk(yacht);
break;
case BUMPER:
ServerYacht collidedYacht = checkYachtCollision(yacht, true);
if (collidedYacht != null) {
yacht.powerDown();
boatTempShutDown(collidedYacht);
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
notifyMessageListeners(
MessageFactory.makeStatusEffectMessage(collidedYacht, powerUp));
}
break;
case RANDOM:
yacht.setPowerUpSpeedMultiplier(BAD_RANDOM_SPEED_PENALTY);
} }
} }
} }
/**
* Powers up a thisYacht with the given token type.
*
* @param thisYacht The yacht to be powered up
* @param collidedToken The token which this thisYacht collided with
*/
private void powerUpYacht(ServerYacht thisYacht, Token collidedToken) {
//The random token has a 50% chance of becoming another token else becoming a speed detriment!
if (collidedToken.getTokenType() == TokenType.RANDOM && new Random().nextBoolean()) {
collidedToken.realiseRandom();
}
//If another yacht has the wind walker token, They should be powered down. Only one allowed!
else if (collidedToken.getTokenType() == TokenType.WIND_WALKER) {
for (ServerYacht otherYacht : yachts.values()) {
if (otherYacht != thisYacht && otherYacht.getPowerUp() == TokenType.WIND_WALKER) {
powerDownYacht(otherYacht);
}
}
}
thisYacht.powerUp(collidedToken.getTokenType());
String logMessage =
thisYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType().getName()
+ " token";
notifyMessageListeners(
MessageFactory.makeChatterMessage(thisYacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(MessageFactory.makePickupMessage(thisYacht, collidedToken));
logger.debug(
"Yacht: " + thisYacht.getShortName() + " got powerup " + collidedToken.getTokenType());
}
private void powerDownYacht(ServerYacht yacht) {
String logMessage =
yacht.getBoatName() + "'s " + yacht.getPowerUp().getName() + " expired";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
yacht.powerDown();
}
// TODO: 23/09/17 wmu16 - This is a hacky way to have the boat power down. Need some sort of separation between token and status effect :/
/**
* Disables the given boat for BUMPER_DISABLE_TIME ms.
*
* @param yacht The yacht to disable
*/
private void boatTempShutDown(ServerYacht yacht) {
yacht.setPowerUpSpeedMultiplier(0d);
Timer shutDownTimer = new Timer("Shutdown Timer");
shutDownTimer.schedule(new TimerTask() {
@Override
public void run() {
yacht.powerDown(); //Note this actually resets the boat to normal.
}
}, BUMPER_DISABLE_TIME);
}
/**
* Checks how long a powerup has been active for. If it has exceeded its timeout, it powers the
* yacht down.
*
* @param yacht The yacht to check to power down
*/
private void checkPowerUpTimeout(ServerYacht yacht) {
if (yacht.getPowerUp() != null) {
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp()
.getTimeout()) {
powerDownYacht(yacht);
}
}
}
/**
* This function changes the wind to be at an angle that causes the yacht in question to be at
* its fastest velocity
*
* @param yacht The yacht to fix the wind for
*/
private void windWalk(ServerYacht yacht) {
Double optimalAngle = PolarTable.getOptimalAngle();
Double heading = yacht.getHeading();
windDirection = (double) Math.floorMod(Math.round(heading + optimalAngle), 360L);
}
/** /**
* Check if the yacht has crossed the course limit * Check if the yacht has crossed the course limit
* *
@@ -356,13 +527,15 @@ public class GameState implements Runnable {
} }
/** /**
* Checks all tokensInPlay to see if a yacht has picked one up * Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up
* @return Token which was collided with * in the appropriate way
* @param serverYacht The yacht to check for collision with a token * @param yacht The yacht to check for collision with a token
*
* @return The token collided with
*/ */
private static Token checkTokenPickUp(ServerYacht serverYacht) { private Token checkTokenPickUp(ServerYacht yacht) {
for (Token token : tokensInPlay) { for (Token token : tokensInPlay) {
Double distance = GeoUtility.getDistance(token, serverYacht.getLocation()); Double distance = GeoUtility.getDistance(token, yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { if (distance < YACHT_COLLISION_DISTANCE) {
return token; return token;
} }
@@ -385,9 +558,8 @@ public class GameState implements Runnable {
*/ */
public static void checkCollision(ServerYacht serverYacht) { public static void checkCollision(ServerYacht serverYacht) {
//Yacht Collision //Yacht Collision
ServerYacht collidedYacht = checkYachtCollision(serverYacht); ServerYacht collidedYacht = checkYachtCollision(serverYacht, false);
Mark collidedMark = checkMarkCollision(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht);
Token collidedToken = checkTokenPickUp(serverYacht);
if (collidedYacht != null) { if (collidedYacht != null) {
GeoPoint originalLocation = serverYacht.getLocation(); GeoPoint originalLocation = serverYacht.getLocation();
@@ -430,50 +602,36 @@ public class GameState implements Runnable {
); );
notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht)); notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
} }
//Token Collision
if (collidedToken != null) {
if (collidedToken.getTokenType() == TokenType.RANDOM) {
collidedToken.realiseRandom();
}
sendServerMessage(serverYacht.getSourceId(),
serverYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType()
.getName() + " token");
tokensInPlay.remove(collidedToken);
serverYacht.powerUp(collidedToken.getTokenType());
logger.debug("Yacht: " + serverYacht.getShortName() + " got powerup " + collidedToken
.getTokenType());
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(MessageFactory.makePickupMessage(serverYacht, collidedToken));
}
} }
private void updateVelocity(ServerYacht yacht) { private void updateVelocity(ServerYacht yacht) {
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier * yacht.getMaxSpeedMultiplier(); Double maxBoatSpeed =
if (yacht.getPowerUp() != null) { GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht
if (yacht.getPowerUp().equals(TokenType.BOOST)) { .getPowerUpSpeedMultiplier() * yacht.getBoatTypeSpeedMultiplier();
maxBoatSpeed *= VELOCITY_BOOST_MULTIPLIER;
}
}
Double currentVelocity = yacht.getCurrentVelocity(); Double currentVelocity = yacht.getCurrentVelocity();
// TODO: 15/08/17 remove magic numbers from these equations. // TODO: 15/08/17 remove magic numbers from these equations.
if (yacht.getSailIn()) { if (yacht.getSailIn()) {
if (currentVelocity < maxBoatSpeed - 500) { if (currentVelocity < maxBoatSpeed - 500) {
yacht.changeVelocity((maxBoatSpeed / 100) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(maxBoatSpeed / 100) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity > maxBoatSpeed + 500) { } else if (currentVelocity > maxBoatSpeed + 500) {
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
} else { } else {
yacht.setCurrentVelocity((maxBoatSpeed) * yacht.getAccelerationMultiplier()); yacht
.setCurrentVelocity((maxBoatSpeed) * yacht.getBoatTypeAccelerationMultiplier());
} }
} else { } else {
if (currentVelocity > 3000) { if (currentVelocity > 3000) {
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity > 100) { } else if (currentVelocity > 100) {
yacht.changeVelocity((-currentVelocity / 50) * yacht.getAccelerationMultiplier()); yacht.changeVelocity(
(-currentVelocity / 50) * yacht.getBoatTypeAccelerationMultiplier());
} else if (currentVelocity <= 100) { } else if (currentVelocity <= 100) {
yacht.setCurrentVelocity(0d); yacht.setCurrentVelocity(0d);
} }
@@ -536,7 +694,10 @@ public class GameState implements Runnable {
if (hasProgressed) { if (hasProgressed) {
if (currentMarkSeqID != 0 && !markOrder.isLastMark(currentMarkSeqID)) { if (currentMarkSeqID != 0 && !markOrder.isLastMark(currentMarkSeqID)) {
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed leg " + yacht.getLegNumber());
String logMessage = yacht.getBoatName() + " passed leg " + yacht.getLegNumber();
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
} }
yacht.incrementLegNumber(); yacht.incrementLegNumber();
sendMarkRoundingMessage(yacht); sendMarkRoundingMessage(yacht);
@@ -572,7 +733,9 @@ public class GameState implements Runnable {
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
yacht.setClosestCurrentMark(mark1); yacht.setClosestCurrentMark(mark1);
yacht.setBoatStatus(BoatStatus.RACING); yacht.setBoatStatus(BoatStatus.RACING);
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed start line"); String logMessage = yacht.getBoatName() + " passed start line";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
return true; return true;
} }
} }
@@ -676,7 +839,10 @@ public class GameState implements Runnable {
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
yacht.setClosestCurrentMark(mark1); yacht.setClosestCurrentMark(mark1);
yacht.setBoatStatus(BoatStatus.FINISHED); yacht.setBoatStatus(BoatStatus.FINISHED);
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed finish line");
String logMessage = yacht.getBoatName() + " passed finish line";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
return true; return true;
} }
} }
@@ -699,6 +865,7 @@ public class GameState implements Runnable {
String name = new String(customizeData); String name = new String(customizeData);
playerYacht.setBoatName(name); playerYacht.setBoatName(name);
} else if (requestType.equals(CustomizeRequestType.COLOR)) { } else if (requestType.equals(CustomizeRequestType.COLOR)) {
//This low level stuff shouldnt be here alistair! In fact no logic LIKE THIS should! - wmu16
int red = customizeData[0] & 0xFF; int red = customizeData[0] & 0xFF;
int green = customizeData[1] & 0xFF; int green = customizeData[1] & 0xFF;
int blue = customizeData[2] & 0xFF; int blue = customizeData[2] & 0xFF;
@@ -711,7 +878,7 @@ public class GameState implements Runnable {
} }
private static Mark checkMarkCollision(ServerYacht yacht) { private static Mark checkMarkCollision(ServerYacht yacht) {
Set<Mark> marksInRace = GameState.getMarks(); Set<Mark> marksInRace = new HashSet<>(marks);
for (Mark mark : marksInRace) { for (Mark mark : marksInRace) {
if (GeoUtility.getDistance(yacht.getLocation(), mark) if (GeoUtility.getDistance(yacht.getLocation(), mark)
<= MARK_COLLISION_DISTANCE) { <= MARK_COLLISION_DISTANCE) {
@@ -740,15 +907,22 @@ public class GameState implements Runnable {
* Collision detection which iterates through all the yachts and check if any yacht collided * Collision detection which iterates through all the yachts and check if any yacht collided
* with this yacht. Return collided yacht or null if no collision. * with this yacht. Return collided yacht or null if no collision.
* *
* UPDATE: HACK!!! wmu16 - forBumperCollision is (the goddamn dirtiest) dirty flag to fix a
* weird bug where the bumper collision would not be registerd but the knock back collision would.
* In other words, only set the 'forBumperCollision' flag true if used for the bumper power up.
*
* @return yacht to compare to all other yachts. * @return yacht to compare to all other yachts.
*/ */
private static ServerYacht checkYachtCollision(ServerYacht yacht) { private static ServerYacht checkYachtCollision(ServerYacht yacht, Boolean forBumperCollision) {
Double collisionDistance =
(forBumperCollision) ? YACHT_COLLISION_DISTANCE + 2.5 : YACHT_COLLISION_DISTANCE;
for (ServerYacht otherYacht : GameState.getYachts().values()) { for (ServerYacht otherYacht : GameState.getYachts().values()) {
if (otherYacht != yacht) { if (otherYacht != yacht) {
Double distance = GeoUtility Double distance = GeoUtility
.getDistance(otherYacht.getLocation(), yacht.getLocation()); .getDistance(otherYacht.getLocation(), yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { ;
if (distance < collisionDistance) {
return otherYacht; return otherYacht;
} }
} }
@@ -784,13 +958,6 @@ public class GameState implements Runnable {
roundingMark.getSourceID())); roundingMark.getSourceID()));
} }
public static void sendServerMessage(Integer messageType, String message) {
notifyMessageListeners(new ChatterMessage(
messageType, "SERVER: " + message
));
}
public static void processChatter(ChatterMessage chatterMessage, boolean isHost) { public static void processChatter(ChatterMessage chatterMessage, boolean isHost) {
String chatterText = chatterMessage.getMessage(); String chatterText = chatterMessage.getMessage();
String[] words = chatterText.split("\\s+"); String[] words = chatterText.split("\\s+");
@@ -798,17 +965,19 @@ public class GameState implements Runnable {
switch (words[2].trim()) { switch (words[2].trim()) {
case "/speed": case "/speed":
try { try {
setSpeedMultiplier(Double.valueOf(words[3])); serverSpeedMultiplier = Double.valueOf(words[3]);
sendServerMessage(chatterMessage.getMessage_type(), String logMessage = "Speed modifier set to x" + words[3];
"Speed modifier set to x" + words[3]); notifyMessageListeners(MessageFactory
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
} catch (Exception e) { } catch (Exception e) {
Logger logger = LoggerFactory.getLogger(GameState.class); Logger logger = LoggerFactory.getLogger(GameState.class);
logger.error("cannot parse >speed value"); logger.error("cannot parse >speed value");
} }
return; return;
case "/finish": case "/finish":
sendServerMessage(chatterMessage.getMessage_type(), String logMessage = "Game will now finish";
"Game will now finish"); notifyMessageListeners(MessageFactory
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
endRace(); endRace();
return; return;
} }
@@ -865,11 +1034,7 @@ public class GameState implements Runnable {
currentStage = GameStages.FINISHED; currentStage = GameStages.FINISHED;
} }
public static void setSpeedMultiplier (double multiplier) { public static double getServerSpeedMultiplier() {
speedMultiplier = multiplier; return serverSpeedMultiplier;
}
public static double getSpeedMultiplier () {
return speedMultiplier;
} }
} }
@@ -30,9 +30,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
private static final int PORT = 4942; private static final int PORT = 4942;
private static final Integer CLIENT_UPDATES_PER_SECOND = 60; private static final Integer CLIENT_UPDATES_PER_SECOND = 60;
private static final int MAX_WIND_SPEED = 12000;
private static final int MIN_WIND_SPEED = 8000;
private boolean terminated; private boolean terminated;
private Thread thread; private Thread thread;
@@ -172,63 +169,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
} }
} }
private static void updateWind(){
Integer direction = GameState.getWindDirection().intValue();
Integer windSpeed = GameState.getWindSpeedMMS().intValue();
Random random = new Random();
if (Math.floorMod(random.nextInt(), 2) == 0){
direction += random.nextInt(4);
windSpeed += random.nextInt(20) + 459;
}
else{
direction -= random.nextInt(4);
windSpeed -= random.nextInt(20) + 459;
}
direction = Math.floorMod(direction, 360);
if (windSpeed > MAX_WIND_SPEED){
windSpeed -= random.nextInt(500);
}
if (windSpeed <= MIN_WIND_SPEED){
windSpeed += random.nextInt(500);
}
GameState.setWindSpeed(Double.valueOf(windSpeed));
GameState.setWindDirection(direction.doubleValue());
}
// TODO: 29/08/17 wmu16 - This sort of update should be in game state
private static void startUpdatingWind(){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
updateWind();
}
}, 0, 500);
}
/**
* Start spawning coins every 60s after the first minute
*/
private void startSpawningTokens() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
GameState.spawnNewToken();
broadcastMessage(MessageFactory.getRaceXML());
}
}, 10000, 60000);
}
/** /**
* A client has tried to connect to the server * A client has tried to connect to the server
* *
@@ -1,6 +1,20 @@
package seng302.gameServer; package seng302.gameServer;
import seng302.gameServer.messages.*; import seng302.gameServer.messages.*;
import java.util.ArrayList;
import java.util.List;
import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.BoatSubMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.RaceStartNotificationType;
import seng302.gameServer.messages.RaceStartStatusMessage;
import seng302.gameServer.messages.RaceStatus;
import seng302.gameServer.messages.RaceStatusMessage;
import seng302.gameServer.messages.RaceType;
import seng302.gameServer.messages.XMLMessage;
import seng302.gameServer.messages.XMLMessageSubType;
import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.gameServer.messages.YachtEventType;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.ServerYacht; import seng302.model.ServerYacht;
import seng302.model.stream.xml.generator.RaceXMLTemplate; import seng302.model.stream.xml.generator.RaceXMLTemplate;
@@ -146,6 +160,14 @@ public class MessageFactory {
return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION); return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION);
} }
/**
* Constructs a message to be sent out whenever a yacht picks up a boost
*
* @param serverYacht The yacht that has picked up a power up
* @param token The token which they picked up
* @return The corresponding YachtEventCodeMessage
*/
public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) { public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) {
YachtEventType yachtEventType = null; YachtEventType yachtEventType = null;
switch (token.getTokenType()) { switch (token.getTokenType()) {
@@ -167,4 +189,39 @@ public class MessageFactory {
} }
return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType); return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType);
} }
/**
* Constructs a message representing a certain buff / debuff for a given yacht. For now this is
* just for the bumper debuff so the affected boat is aware that it has been crashed. This could
* however be extended to render affects for all boats given a certain debuff.
*
* @param yacht The yacht affected by some status
* @param token The token indicating what status they have
* @return A YachtEventCodeMessage
*/
public static YachtEventCodeMessage makeStatusEffectMessage(ServerYacht yacht,
TokenType token) {
YachtEventType yachtEventType = null;
switch (token) {
case BUMPER:
yachtEventType = YachtEventType.BUMPER_CRASH;
break;
}
return new YachtEventCodeMessage(yacht.getSourceId(), yachtEventType);
}
/**
* Constructs a message to be sent out when a given yacht powers down (From a boost of any type)
*
* @param yacht The yacht that is powering down
* @return A YachtEventCodeMessage representing this action
*/
public static YachtEventCodeMessage makePowerDownMessage(ServerYacht yacht) {
return new YachtEventCodeMessage(yacht.getSourceId(), YachtEventType.POWER_DOWN);
}
public static ChatterMessage makeChatterMessage(Integer messageType, String message) {
return new ChatterMessage(messageType, "SERVER: " + message);
}
} }
@@ -71,7 +71,6 @@ public class ServerToClientThread implements Runnable {
private List<ConnectionListener> connectionListeners = new ArrayList<>(); private List<ConnectionListener> connectionListeners = new ArrayList<>();
private DisconnectListener disconnectListener; private DisconnectListener disconnectListener;
private ServerYacht yacht;
private Player player; private Player player;
private SimpleObjectProperty<RaceXMLData> raceXMLProperty = new SimpleObjectProperty<>(); private SimpleObjectProperty<RaceXMLData> raceXMLProperty = new SimpleObjectProperty<>();
@@ -97,11 +96,11 @@ public class ServerToClientThread implements Runnable {
} }
private void setUpPlayer(){ private void setUpPlayer(){
String fName = "Player" + GameState.getNumberOfPlayers().toString(); String shortName = "P" + sourceId;
String lName = ""; String longName = "Player " + sourceId;
ServerYacht yacht = new ServerYacht( ServerYacht yacht = new ServerYacht(
BoatMeshType.DINGHY, sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ" BoatMeshType.DINGHY, sourceId, sourceId.toString(), shortName, longName, "NZ");
);
player = new Player(socket, yacht); player = new Player(socket, yacht);
GameState.addYacht(sourceId, yacht); GameState.addYacht(sourceId, yacht);
@@ -293,10 +292,6 @@ public class ServerToClientThread implements Runnable {
return socket; return socket;
} }
public ServerYacht getYacht() {
return yacht;
}
public void addConnectionListener(ConnectionListener listener) { public void addConnectionListener(ConnectionListener listener) {
connectionListeners.add(listener); connectionListeners.add(listener);
} }
@@ -40,7 +40,7 @@ public class ChatterMessage extends Message {
return message; return message;
} }
public int getMessage_type() { public int getMessageType() {
return message_type; return message_type;
} }
} }
@@ -1,7 +1,7 @@
package seng302.gameServer.messages; package seng302.gameServer.messages;
/** /**
* Created by wmu16 on 11/09/17. * Enum for different event types for the yacht
*/ */
public enum YachtEventType { public enum YachtEventType {
COLLISION(33), COLLISION(33),
@@ -9,7 +9,10 @@ public enum YachtEventType {
TOKEN_BUMPER(35), TOKEN_BUMPER(35),
TOKEN_HANDLING(36), TOKEN_HANDLING(36),
TOKEN_WIND_WALKER(37), TOKEN_WIND_WALKER(37),
TOKEN_RANDOM(38); TOKEN_RANDOM(38),
POWER_DOWN(39),
BUMPER_CRASH(40);
private int code; private int code;
@@ -19,6 +19,7 @@ import javafx.scene.paint.Color;
import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.Function;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.model.token.TokenType;
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
import seng302.model.token.TokenType; import seng302.model.token.TokenType;
import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.BoatObject;
@@ -41,11 +42,26 @@ public class ClientYacht extends Observable {
void notifyRounding(ClientYacht yacht, int legNumber); void notifyRounding(ClientYacht yacht, int legNumber);
} }
@FunctionalInterface
public interface ColorChangeListener {
void notifyColorChange(ClientYacht yacht);
}
//This notifies RaceViewController so it can display icon - wmu16
@FunctionalInterface @FunctionalInterface
public interface PowerUpListener { public interface PowerUpListener {
void notifyPowerUp(ClientYacht yacht, TokenType tokenType); void notifyPowerUp(ClientYacht yacht, TokenType tokenType);
} }
//This notifies RaceViewController so it can remove token icon - wmu16
@FunctionalInterface
public interface PowerDownListener {
void notifyPowerDown(ClientYacht yacht);
}
private Logger logger = LoggerFactory.getLogger(ClientYacht.class); private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
@@ -76,6 +92,9 @@ public class ClientYacht extends Observable {
private List<YachtLocationListener> locationListeners = new ArrayList<>(); private List<YachtLocationListener> locationListeners = new ArrayList<>();
private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>(); private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>();
private List<PowerUpListener> powerUpListeners = new ArrayList<>(); private List<PowerUpListener> powerUpListeners = new ArrayList<>();
private List<PowerDownListener> powerDownListeners = new ArrayList<>();
private List<ColorChangeListener> colorChangeListeners = new ArrayList<>();
private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
@@ -219,6 +238,21 @@ public class ClientYacht extends Observable {
this.position = position; this.position = position;
} }
/**
* Powers down the boat and notifies the raceViewController to display
*/
public void powerDown() {
this.powerUp = null;
for (PowerDownListener listener : powerDownListeners) {
listener.notifyPowerDown(this);
}
}
/**
* powers up the boat and notifies the raceViewController to display
*
* @param tokenType The type of token that this boat is being powered up with
*/
public void setPowerUp(TokenType tokenType) { public void setPowerUp(TokenType tokenType) {
this.powerUp = tokenType; this.powerUp = tokenType;
for (PowerUpListener listener : powerUpListeners) { for (PowerUpListener listener : powerUpListeners) {
@@ -279,6 +313,9 @@ public class ClientYacht extends Observable {
public void setColour(Color colour) { public void setColour(Color colour) {
this.colour = colour; this.colour = colour;
for (ColorChangeListener listener : colorChangeListeners) {
listener.notifyColorChange(this);
}
} }
public void updateLocation(double lat, double lng, double heading, double velocity) { public void updateLocation(double lat, double lng, double heading, double velocity) {
@@ -308,6 +345,14 @@ public class ClientYacht extends Observable {
powerUpListeners.add(listener); powerUpListeners.add(listener);
} }
public void addPowerDownListener(PowerDownListener listener) {
powerDownListeners.add(listener);
}
public void addColorChangeListener(ColorChangeListener listener) {
colorChangeListeners.add(listener);
}
public void removeMarkRoundingListener(MarkRoundingListener listener) { public void removeMarkRoundingListener(MarkRoundingListener listener) {
markRoundingListeners.remove(listener); markRoundingListeners.remove(listener);
} }
+10 -1
View File
@@ -30,7 +30,12 @@ public class GameKeyBind {
keys.add(KeyCode.ENTER); keys.add(KeyCode.ENTER);
keys.add(KeyCode.PAGE_UP); keys.add(KeyCode.PAGE_UP);
keys.add(KeyCode.PAGE_DOWN); keys.add(KeyCode.PAGE_DOWN);
for (int i = 0; i < 7; i++) { keys.add(KeyCode.F1);
keys.add(KeyCode.D);
keys.add(KeyCode.A);
keys.add(KeyCode.W);
keys.add(KeyCode.S);
for (int i = 0; i < 12; i++) {
actionToKeyMap.put(KeyAction.getType(i + 1), keys.get(i)); actionToKeyMap.put(KeyAction.getType(i + 1), keys.get(i));
keyToActionMap.put(keys.get(i), KeyAction.getType(i + 1)); keyToActionMap.put(keys.get(i), KeyAction.getType(i + 1));
} }
@@ -47,6 +52,10 @@ public class GameKeyBind {
return instance.actionToKeyMap.get(keyAction); return instance.actionToKeyMap.get(keyAction);
} }
public KeyAction getKeyAction(KeyCode keyCode) {
return instance.keyToActionMap.get(keyCode);
}
/** /**
* Binds a key to a key action * Binds a key to a key action
* *
+6 -1
View File
@@ -10,7 +10,12 @@ public enum KeyAction {
SAILS_STATE(4), SAILS_STATE(4),
TACK_GYBE(5), TACK_GYBE(5),
UPWIND(6), UPWIND(6),
DOWNWIND(7); DOWNWIND(7),
VIEW(8),
RIGHT(9),
LEFT(10),
FORWARD(11),
BACKWARD(12);
private final int type; private final int type;
private static final Map<Integer, KeyAction> intToTypeMap = new HashMap<>(); private static final Map<Integer, KeyAction> intToTypeMap = new HashMap<>();
+37 -4
View File
@@ -4,7 +4,9 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
/** /**
* A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised * A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised
@@ -17,6 +19,7 @@ public final class PolarTable {
private static HashMap<Double, HashMap<Double, Double>> polarTable; private static HashMap<Double, HashMap<Double, Double>> polarTable;
private static HashMap<Double, HashMap<Double, Double>> upwindOptimal; private static HashMap<Double, HashMap<Double, Double>> upwindOptimal;
private static HashMap<Double, HashMap<Double, Double>> downwindOptimal; private static HashMap<Double, HashMap<Double, Double>> downwindOptimal;
private static Double optimalAngle;
private static int upTwaIndex; private static int upTwaIndex;
private static int dnTwaIndex; private static int dnTwaIndex;
@@ -33,11 +36,13 @@ public final class PolarTable {
upwindOptimal = new HashMap<>(); upwindOptimal = new HashMap<>();
downwindOptimal = new HashMap<>(); downwindOptimal = new HashMap<>();
String line; String line = null;
String check;
Boolean isHeaderLine = true; Boolean isHeaderLine = true;
try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) { try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) {
while ((line = br.readLine()) != null) { while ((check = br.readLine()) != null) {
line = check;
String[] thisLine = line.split(","); String[] thisLine = line.split(",");
//Initial line in file //Initial line in file
@@ -66,7 +71,10 @@ public final class PolarTable {
upwindOptimal.put(thisWindSpeed, thisUpWindPolar); upwindOptimal.put(thisWindSpeed, thisUpWindPolar);
downwindOptimal.put(thisWindSpeed, thisDnWindPolar); downwindOptimal.put(thisWindSpeed, thisDnWindPolar);
} }
} }
getMaxSpeedAngle(line);
} catch (IOException e) { } catch (IOException e) {
System.out.println("[PolarTable] IO exception"); System.out.println("[PolarTable] IO exception");
@@ -74,6 +82,27 @@ public final class PolarTable {
} }
/**
* Passes the final line of the polar table and iterates over the speeds for each
* angle, velocity pair to find the angle that produces the highest velocity
*
* @param line The last line of the polar file
*/
private static void getMaxSpeedAngle(String line) {
String[] theLastLine = line.split(",");
Double maxWindVal = Double.parseDouble(theLastLine[0]);
Double optimalAngle = Double.parseDouble(theLastLine[1]);
Double maxSpeed = Double.parseDouble(theLastLine[2]);
for (Map.Entry<Double, Double> entry : polarTable.get(maxWindVal).entrySet()) {
if (entry.getValue() > maxSpeed) {
maxSpeed = entry.getValue();
optimalAngle = entry.getKey();
}
}
PolarTable.optimalAngle = optimalAngle;
}
/** /**
* Parses the header line of a polar file * Parses the header line of a polar file
@@ -85,14 +114,18 @@ public final class PolarTable {
String thisItem = thisLine[i]; String thisItem = thisLine[i];
if (thisItem.toLowerCase().startsWith("uptwa")) { if (thisItem.toLowerCase().startsWith("uptwa")) {
upTwaIndex = i; upTwaIndex = i;
} } else if (thisItem.toLowerCase().startsWith("dntwa")) {
else if (thisItem.toLowerCase().startsWith("dntwa")) {
dnTwaIndex = i; dnTwaIndex = i;
} }
} }
} }
public static Double getOptimalAngle() {
return optimalAngle;
}
/** /**
* @return The entire polar table * @return The entire polar table
*/ */
+57 -18
View File
@@ -23,9 +23,9 @@ public class ServerYacht {
//Boat info //Boat info
private BoatMeshType boatType; private BoatMeshType boatType;
private Double turnStep = 5.0; private Double turnStep = 5.0;
private Double maxSpeedMultiplier = 1.0; private Double boatTypeSpeedMultiplier = 1.0;
private Double turnStepMultiplier = 1.0; private Double boatTypeTurnStepMultiplier = 1.0;
private Double accelerationMultiplier = 1.0; private Double boatTypeAccelerationMultiplier = 1.0;
private Integer sourceId; private Integer sourceId;
private String hullID; //matches HullNum in the XML spec. private String hullID; //matches HullNum in the XML spec.
private String shortName; private String shortName;
@@ -55,6 +55,8 @@ public class ServerYacht {
//PowerUp //PowerUp
private TokenType powerUp; private TokenType powerUp;
private Long powerUpStartTime; private Long powerUpStartTime;
private Double powerUpSpeedMultiplier;
private Integer powerUpHandlingMultiplier;
//turning mode //turning mode
private Boolean continuouslyTurning; private Boolean continuouslyTurning;
@@ -78,11 +80,11 @@ public class ServerYacht {
this.legNumber = 0; this.legNumber = 0;
this.boatColor = Colors.getColor(sourceId - 1); this.boatColor = Colors.getColor(sourceId - 1);
this.powerUp = null; this.powerUp = null;
this.powerUpSpeedMultiplier = 1d;
this.powerUpHandlingMultiplier = 1;
this.hasEnteredRoundingZone = false; this.hasEnteredRoundingZone = false;
this.hasPassedLine = false; this.hasPassedLine = false;
this.hasPassedThroughGate = false; this.hasPassedThroughGate = false;
this.continuouslyTurning = false; this.continuouslyTurning = false;
} }
@@ -110,13 +112,33 @@ public class ServerYacht {
location = geoPoint; location = geoPoint;
} }
/**
* Powers up a yacht with a given yacht, only after powering it down first to avoid double power
* ups
*
* @param powerUp The given power up
*/
public void powerUp(TokenType powerUp) { public void powerUp(TokenType powerUp) {
powerDown();
switch (powerUp) {
case BOOST:
powerUpSpeedMultiplier = GameState.VELOCITY_BOOST_MULTIPLIER;
break;
case HANDLING:
powerUpHandlingMultiplier = GameState.HANDLING_BOOST_MULTIPLIER;
break;
}
this.powerUp = powerUp; this.powerUp = powerUp;
powerUpStartTime = System.currentTimeMillis(); powerUpStartTime = System.currentTimeMillis();
} }
/**
* Powers down a yacht, returning its power multipliers back to 1
*/
public void powerDown() { public void powerDown() {
this.powerUp = null; this.powerUp = null;
this.powerUpSpeedMultiplier = 1d;
this.powerUpHandlingMultiplier = 1;
} }
public Long getPowerUpStartTime() { public Long getPowerUpStartTime() {
@@ -133,7 +155,7 @@ public class ServerYacht {
* @param amount the amount by which to adjust the boat heading. * @param amount the amount by which to adjust the boat heading.
*/ */
public void adjustHeading(Double amount) { public void adjustHeading(Double amount) {
Double newVal = heading + (amount * turnStepMultiplier); Double newVal = heading + amount * powerUpHandlingMultiplier * boatTypeTurnStepMultiplier;
lastHeading = heading; lastHeading = heading;
heading = (double) Math.floorMod(newVal.longValue(), 360L); heading = (double) Math.floorMod(newVal.longValue(), 360L);
} }
@@ -156,11 +178,11 @@ public class ServerYacht {
/** /**
* Enables the boats auto pilot feature, which will move the boat towards a given heading. * Enables the boats auto pilot feature, which will move the boat towards a given heading.
* *
* @param thisHeading The heading to move the boat towards. * @param newHeading The heading to move the boat towards.
*/ */
private void setAutoPilot(Double thisHeading) { private void setAutoPilot(Double newHeading) {
isAuto = true; isAuto = true;
autoHeading = thisHeading; autoHeading = newHeading;
} }
/** /**
@@ -178,8 +200,9 @@ public class ServerYacht {
if (isAuto) { if (isAuto) {
turnTowardsHeading(autoHeading); turnTowardsHeading(autoHeading);
if (Math.abs(heading - autoHeading) if (Math.abs(heading - autoHeading)
<= turnStep) { //Cancel when within 1 turn step of target. <= turnStep*1.5) {
isAuto = false; isAuto = false;
setHeading(autoHeading);
} }
} }
} }
@@ -265,7 +288,7 @@ public class ServerYacht {
// Take optimal heading and turn into a boat heading rather than a wind heading. // Take optimal heading and turn into a boat heading rather than a wind heading.
optimalHeading = optimalHeading =
optimalHeading + GameState.getWindDirection(); (optimalHeading + GameState.getWindDirection()) % 360;
setAutoPilot(optimalHeading); setAutoPilot(optimalHeading);
} }
@@ -434,18 +457,18 @@ public class ServerYacht {
} }
public void setBoatType(BoatMeshType boatType) { public void setBoatType(BoatMeshType boatType) {
this.accelerationMultiplier = boatType.accelerationMultiplier; this.boatTypeAccelerationMultiplier = boatType.accelerationMultiplier;
this.maxSpeedMultiplier = boatType.maxSpeedMultiplier; this.boatTypeSpeedMultiplier = boatType.maxSpeedMultiplier;
this.turnStepMultiplier = boatType.turnStep; this.boatTypeTurnStepMultiplier = boatType.turnStep;
this.boatType = boatType; this.boatType = boatType;
} }
public Double getMaxSpeedMultiplier() { public Double getBoatTypeSpeedMultiplier() {
return maxSpeedMultiplier; return boatTypeSpeedMultiplier;
} }
public Double getAccelerationMultiplier(){ public Double getBoatTypeAccelerationMultiplier() {
return accelerationMultiplier; return boatTypeAccelerationMultiplier;
} }
@@ -456,4 +479,20 @@ public class ServerYacht {
public void setContinuouslyTurning(Boolean continuouslyTurning) { public void setContinuouslyTurning(Boolean continuouslyTurning) {
this.continuouslyTurning = continuouslyTurning; this.continuouslyTurning = continuouslyTurning;
} }
public Double getPowerUpSpeedMultiplier() {
return powerUpSpeedMultiplier;
}
public void setPowerUpSpeedMultiplier(Double powerUpSpeedMultiplier) {
this.powerUpSpeedMultiplier = powerUpSpeedMultiplier;
}
public Integer getPowerUpHandlingMultiplier() {
return powerUpHandlingMultiplier;
}
public void setPowerUpHandlingMultiplier(Integer powerUpHandlingMultiplier) {
this.powerUpHandlingMultiplier = powerUpHandlingMultiplier;
}
} }
@@ -13,7 +13,9 @@ import seng302.model.stream.xml.parser.RaceXMLData;
*/ */
public class MarkOrder { public class MarkOrder {
private List<CompoundMark> raceMarkOrder; private List<CompoundMark> raceMarkOrder;
private List<CompoundMark> orderedUniqueCompoundMarks;
private Logger logger = LoggerFactory.getLogger(MarkOrder.class); private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
private List<Mark> allMarks;
public MarkOrder(RaceXMLData raceXMLData){ public MarkOrder(RaceXMLData raceXMLData){
@@ -39,6 +41,10 @@ public class MarkOrder {
return Collections.unmodifiableList(raceMarkOrder); return Collections.unmodifiableList(raceMarkOrder);
} }
public List<CompoundMark> getOrderedUniqueCompoundMarks() {
return orderedUniqueCompoundMarks;
}
/** /**
* @param seqID The seqID of the current mark the boat is heading to * @param seqID The seqID of the current mark the boat is heading to
* @return A Boolean indicating if this coming mark is the last one (finish line) * @return A Boolean indicating if this coming mark is the last one (finish line)
+19 -1
View File
@@ -15,11 +15,24 @@ public class Token extends GeoPoint {
private TokenType tokenType; private TokenType tokenType;
private Random random = new Random(); private Random random = new Random();
//Constructor for creating a specific type client side
public Token(TokenType tokenType, double lat, double lng) { public Token(TokenType tokenType, double lat, double lng) {
super(lat, lng); super(lat, lng);
this.tokenType = tokenType; this.tokenType = tokenType;
} }
//Making random type server side
public Token(double lat, double lng) {
super(lat, lng);
assignRandomType();
}
//Making random type server side
public Token(GeoPoint geoPoint) {
super(geoPoint.getLat(), geoPoint.getLng());
assignRandomType();
}
public TokenType getTokenType() { public TokenType getTokenType() {
return tokenType; return tokenType;
} }
@@ -40,5 +53,10 @@ public class Token extends GeoPoint {
tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size())); tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size()));
} }
/**
* Exists for testing purposes only
*/
public void assignType(TokenType tokenType) {
this.tokenType = tokenType;
}
} }
@@ -0,0 +1,64 @@
package seng302.utilities;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import seng302.model.GeoPoint;
import seng302.model.mark.CompoundMark;
import seng302.model.token.Token;
/**
* A class for generating and spawning tokens in random locations
* Created by wmu16 on 27/09/17.
*/
public class RandomSpawn {
private static final Integer DEGREES_IN_CIRCLE = 360;
private HashMap<GeoPoint, Double> spawnRadii;
private Object[] spawnCentres;
private Random random;
/**
* @param markOrder this must be the ORDERED list of marks. Better yet UNIQUE to avoid over
* computation
*/
public RandomSpawn(List<CompoundMark> markOrder) {
this.spawnRadii = new HashMap<>();
random = new Random();
spawnRadii = generateSpawnRadii(markOrder);
spawnCentres = spawnRadii.keySet().toArray();
}
private HashMap<GeoPoint, Double> generateSpawnRadii(List<CompoundMark> markOrder) {
HashMap<GeoPoint, Double> spawnRadii = new HashMap<>();
for (int i = 0; i < markOrder.size() - 1; i++) {
GeoPoint spawnCentre = GeoUtility.getDirtyMidPoint(
markOrder.get(i).getMidPoint(),
markOrder.get(i + 1).getMidPoint());
Double distance = GeoUtility.getDistance(spawnCentre, markOrder.get(i).getMidPoint());
spawnRadii.put(spawnCentre, distance);
}
return spawnRadii;
}
/**
* @return A random token type at a random location in a random radii of the set of possible
* radii
*/
public Token getRandomToken() {
GeoPoint randomSpawnCentre = (GeoPoint) spawnCentres[random.nextInt(spawnCentres.length)];
Double spawnRadius = spawnRadii.get(randomSpawnCentre);
Double randomDistance = spawnRadius * random.nextDouble();
Double randomAngle = random.nextDouble() * DEGREES_IN_CIRCLE;
GeoPoint randomLocation = GeoUtility
.getGeoCoordinate(randomSpawnCentre, randomAngle, randomDistance);
return new Token(randomLocation);
}
}
@@ -1,14 +1,25 @@
package seng302.visualiser; 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 java.util.Timer;
import java.util.TimerTask;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.util.Pair; import javafx.util.Pair;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
@@ -37,6 +48,7 @@ import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.LobbyController; import seng302.visualiser.controllers.LobbyController;
import seng302.visualiser.controllers.RaceViewController; import seng302.visualiser.controllers.RaceViewController;
import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.controllers.dialogs.PopupDialogController;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -166,10 +178,12 @@ public class GameClient {
private void showConnectionError (String message) { private void showConnectionError (String message) {
Platform.runLater(() -> { Platform.runLater(() -> {
Alert alert = new Alert(AlertType.ERROR); PopupDialogController controller = ViewManager.getInstance().showPopupDialog();
alert.setHeaderText("Connection Error"); controller.setHeader("Oops");
alert.setContentText(message); controller.setContent(message);
alert.showAndWait(); controller.setOptionButtonText("GO HOME");
controller
.setOptionButtonEventHandler(event -> ViewManager.getInstance().goToStartView());
}); });
} }
@@ -413,9 +427,16 @@ public class GameClient {
* @param yachtEventData The YachtEvent data packet * @param yachtEventData The YachtEvent data packet
*/ */
private void processYachtEvent(YachtEventData yachtEventData) { private void processYachtEvent(YachtEventData yachtEventData) {
ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue());
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
showCollisionAlert(yachtEventData); showCollisionAlert(thisYacht);
} else { } else if (yachtEventData.getEventId() == YachtEventType.POWER_DOWN.getCode()) {
thisYacht.powerDown();
Sounds.playTokenPickupSound(); // TODO: 23/09/17 This should be power down sound
} else if (yachtEventData.getEventId() == YachtEventType.BUMPER_CRASH.getCode()) {
showDisableAlert(thisYacht);
} else { //Else all token pickup types
TokenType tokenType = null; TokenType tokenType = null;
if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) {
tokenType = TokenType.BOOST; tokenType = TokenType.BOOST;
@@ -429,36 +450,36 @@ public class GameClient {
tokenType = TokenType.WIND_WALKER; tokenType = TokenType.WIND_WALKER;
} }
showTokenPickUp(tokenType); Sounds.playTokenPickupSound();
allBoatsMap.get(yachtEventData.getSubjectId().intValue()).setPowerUp(tokenType); thisYacht.setPowerUp(tokenType);
} }
} }
/**
* Turns a disabled boat black until the bumper affect wears off
*
* @param yacht The yacht to show as disabled
*/
private void showDisableAlert(ClientYacht yacht) {
Color originalColor = yacht.getColour();
yacht.setColour(Color.BLACK);
Timer disableTimer = new Timer("Disable Timer");
disableTimer.schedule(new TimerTask() {
@Override
public void run() {
yacht.setColour(originalColor);
}
}, GameState.BUMPER_DISABLE_TIME);
}
/** /**
* Tells race view to show a collision animation. * Tells race view to show a collision animation.
*/ */
private void showCollisionAlert(YachtEventData yachtEventData) { private void showCollisionAlert(ClientYacht yacht) {
Sounds.playCrashSound(); Sounds.playCrashSound();
raceState.storeCollision( raceState.storeCollision(yacht);
allBoatsMap.get(
yachtEventData.getSubjectId().intValue()
)
);
}
// TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user
private void showTokenPickUp(TokenType tokenType) {
Sounds.playTokenPickupSound();
switch (tokenType) {
case BOOST:
break;
case HANDLING:
break;
case WIND_WALKER:
break;
case BUMPER:
break;
}
} }
private void formatAndSendChatMessage(String rawChat) { private void formatAndSendChatMessage(String rawChat) {
@@ -1,6 +1,8 @@
package seng302.visualiser; package seng302.visualiser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -8,6 +10,7 @@ import javafx.animation.AnimationTimer;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.geometry.Point3D; import javafx.geometry.Point3D;
import javafx.scene.Camera;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.PerspectiveCamera; import javafx.scene.PerspectiveCamera;
@@ -22,6 +25,9 @@ import javafx.scene.transform.Translate;
import org.fxyz3d.scene.Skybox; import org.fxyz3d.scene.Skybox;
import seng302.gameServer.messages.RoundingSide; import seng302.gameServer.messages.RoundingSide;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.GameKeyBind;
import seng302.model.GeoPoint;
import seng302.model.KeyAction;
import seng302.model.Limit; import seng302.model.Limit;
import seng302.model.ScaledPoint; import seng302.model.ScaledPoint;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
@@ -49,17 +55,27 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType;
public class GameView3D extends GameView{ public class GameView3D extends GameView{
private final double FOV = 60; private final double FOV = 60;
private final double DEFAULT_CAMERA_DEPTH = -125;
private final double DEFAULT_CAMERA_X = 0; private final double DEFAULT_CAMERA_X = 0;
private final double DEFAULT_CAMERA_Y = 100; private final double DEFAULT_CAMERA_Y = 100;
private Group root3D; private Group root3D;
private SubScene view; private SubScene view;
private PerspectiveCamera camera;
private PerspectiveCamera camera2;
private PerspectiveCamera camera3;
private Group gameObjects; private Group gameObjects;
// Cameras
private PerspectiveCamera isometricCam;
private PerspectiveCamera topDownCam;
private PerspectiveCamera chaseCam;
private double bufferSize = 0;
private double canvasWidth = 200;
private double canvasHeight = 200;
private boolean horizontalInversion = false;
private double distanceScaleFactor;
private ScaleDirection scaleDirection;
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
private double referencePointX, referencePointY;
private Group raceBorder = new Group(); private Group raceBorder = new Group();
/* Note that if either of these is null then values for it have not been added and the other /* Note that if either of these is null then values for it have not been added and the other
@@ -75,31 +91,28 @@ public class GameView3D extends GameView{
private Double windDir; private Double windDir;
private Skybox skybox; private Skybox skybox;
private enum ScaleDirection {
HORIZONTAL,
VERTICAL
}
public GameView3D () { public GameView3D () {
canvasWidth = canvasHeight = 220; isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y);
topDownCam = new TopDownCamera();
chaseCam = new ChaseCamera();
camera = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH); for (PerspectiveCamera pc : Arrays.asList(isometricCam, topDownCam, chaseCam)) {
camera.setFarClip(100000); pc.setFarClip(600);
camera.setNearClip(0.1); pc.setNearClip(0.1);
camera.setFieldOfView(FOV); pc.setFieldOfView(FOV);
}
camera2 = new TopDownCamera();
camera2.setFarClip(100000);
camera2.setNearClip(0.1);
camera2.setFieldOfView(FOV);
camera3 = new ChaseCamera();
camera3.setFarClip(100000);
camera3.setNearClip(0.1);
camera3.setFieldOfView(FOV);
gameObjects = new Group(); gameObjects = new Group();
root3D = new Group(camera, gameObjects); root3D = new Group(isometricCam, gameObjects);
view = new SubScene( view = new SubScene(
root3D, 5000, 3000, true, SceneAntialiasing.BALANCED root3D, 5000, 3000, true, SceneAntialiasing.BALANCED
); );
view.setCamera(camera); view.setCamera(isometricCam);
camera.getTransforms().add(new Rotate(30, new Point3D(1,0,0)));
skybox = new Skybox(new Image(getClass().getResourceAsStream("/images/skybox.jpg")), 100000, camera); skybox = new Skybox(new Image(getClass().getResourceAsStream("/images/skybox.jpg")), 100000, camera);
skybox.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS)); skybox.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS));
@@ -274,48 +287,44 @@ public class GameView3D extends GameView{
} }
public void cameraMovement(KeyEvent event) { public void cameraMovement(KeyEvent event) {
switch (event.getCode()) { GameKeyBind keyBinds = GameKeyBind.getInstance();
case NUMPAD8: KeyAction keyPressed = keyBinds.getKeyAction(event.getCode());
view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(1, 0, 0))); if (keyPressed != null) {
break; switch (keyPressed) {
case NUMPAD2: case ZOOM_IN:
view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(1, 0, 0))); ((RaceCamera) view.getCamera()).zoomIn();
break; break;
case NUMPAD4: case ZOOM_OUT:
view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(0, 1, 0))); ((RaceCamera) view.getCamera()).zoomOut();
break; break;
case NUMPAD6: case FORWARD:
view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(0, 1, 0))); ((RaceCamera) view.getCamera()).panUp();
break; break;
case Z: case BACKWARD:
((RaceCamera) view.getCamera()).zoomIn(); ((RaceCamera) view.getCamera()).panDown();
break; break;
case X: case LEFT:
((RaceCamera) view.getCamera()).zoomOut(); ((RaceCamera) view.getCamera()).panLeft();
break; break;
case W: case RIGHT:
((RaceCamera) view.getCamera()).panUp(); ((RaceCamera) view.getCamera()).panRight();
break; break;
case S: case VIEW:
((RaceCamera) view.getCamera()).panDown(); toggleCamera();
break; break;
case A: }
((RaceCamera) view.getCamera()).panLeft(); }
break; }
case D:
((RaceCamera) view.getCamera()).panRight(); private void toggleCamera() {
break; Camera currCamera = view.getCamera();
case F1:
if (view.getCamera().equals(camera)) { if (currCamera.equals(isometricCam)) {
view.setCamera(camera2); view.setCamera(topDownCam);
if (view.getCamera() instanceof TopDownCamera) { } else if (currCamera.equals(topDownCam)) {
((RaceCamera) view.getCamera()).zoomIn(); view.setCamera(chaseCam);
} } else {
} else if (view.getCamera().equals(camera2)) { view.setCamera(isometricCam);
view.setCamera(camera3);
} else {
view.setCamera(camera);
}
} }
} }
@@ -334,16 +343,13 @@ public class GameView3D extends GameView{
wakesGroup.getChildren().add(newBoat.getWake()); wakesGroup.getChildren().add(newBoat.getWake());
wakes.add(newBoat.getWake()); wakes.add(newBoat.getWake());
boatObjectGroup.getChildren().add(newBoat); boatObjectGroup.getChildren().add(newBoat);
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { clientYacht.addLocationListener(this::updateBoatLocation);
BoatObject bo = boatObjects.get(boat); clientYacht.addColorChangeListener(this::updateBoatColor);
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
});
if (clientYacht.getSourceId().equals( if (clientYacht.getSourceId().equals(
ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { ViewManager.getInstance().getGameClient().getServerThread().getClientId())) {
((ChaseCamera) camera3).setPlayerBoat(newBoat, clientYacht); ((ChaseCamera) chaseCam).setPlayerBoat(newBoat);
((TopDownCamera) camera2).setPlayerBoat(newBoat); ((TopDownCamera) topDownCam).setPlayerBoat(newBoat);
} }
} }
Platform.runLater(() -> { Platform.runLater(() -> {
@@ -356,6 +362,23 @@ public class GameView3D extends GameView{
return view; return view;
} }
/**
* Updates the boatObjects color with that of the clientYachts object. Used in notification from
* a listener on this attribute in clientYacht to re paint the boat mesh
*
* @param clientYacht The yacht to update the colour for
*/
private void updateBoatColor(ClientYacht clientYacht) {
boatObjects.get(clientYacht).setFill(clientYacht.getColour());
}
private void updateBoatLocation(ClientYacht boat, Double lat, Double lon, Double heading,
Boolean sailIn, Double velocity) {
BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
}
/** /**
* Adds a border to the GameView and rescales to the size of the border, does not rescale if a * Adds a border to the GameView and rescales to the size of the border, does not rescale if a
* border already exists. Assumes the border is larger than the course. * border already exists. Assumes the border is larger than the course.
@@ -1,22 +1,28 @@
package seng302.visualiser.cameras; package seng302.visualiser.cameras;
import javafx.beans.value.ChangeListener; import java.util.Arrays;
import javafx.beans.value.ObservableValue; import javafx.beans.property.DoubleProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.Point3D; import javafx.geometry.Point3D;
import javafx.scene.PerspectiveCamera; import javafx.scene.PerspectiveCamera;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform; import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate; import javafx.scene.transform.Translate;
import seng302.model.ClientYacht;
import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.BoatObject;
public class ChaseCamera extends PerspectiveCamera implements RaceCamera { public class ChaseCamera extends PerspectiveCamera implements RaceCamera {
private final Double VERTICAL_PAN_LIMIT = 20.0;
private final Double NEAR_ZOOM_LIMIT = -15.0;
private final Double FAR_ZOOM_LIMIT = -125.0;
private final Double ZOOM_STEP = 2.5;
private final Double PAN_STEP = 2.5;
private ObservableList<Transform> transforms; private ObservableList<Transform> transforms;
private BoatObject playerBoat; private BoatObject playerBoat;
private ClientYacht playerYacht;
private Double zoomFactor; private Double zoomFactor;
private Double horizontalPan; private Double horizontalPan;
private Double verticalPan; private Double verticalPan;
@@ -25,97 +31,100 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera {
public ChaseCamera() { public ChaseCamera() {
super(true); super(true);
transforms = this.getTransforms(); transforms = this.getTransforms();
this.zoomFactor = -75.0;
zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0;
this.horizontalPan = 0.0; this.horizontalPan = 0.0;
this.verticalPan = 0.0; this.verticalPan = 0.0;
} }
public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { /**
* Sets a player boat object to observe and update the camera with.
*
* @param playerBoat The player boat to be observed.
*/
public void setPlayerBoat(BoatObject playerBoat) {
this.playerBoat = playerBoat; this.playerBoat = playerBoat;
this.playerYacht = playerYacht;
this.playerYacht.getHeadingProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
repositionCamera();
}
});
this.playerBoat.layoutXProperty().addListener(new ChangeListener<Number>() { for (DoubleProperty o : Arrays
@Override .asList(playerBoat.getRotationProperty(), playerBoat.layoutYProperty(),
public void changed(ObservableValue<? extends Number> observable, Number oldValue, playerBoat.layoutXProperty())) {
Number newValue) { o.addListener((obs, oldVal, newVal) -> repositionCamera());
repositionCamera(); }
}
});
this.playerBoat.layoutYProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
repositionCamera();
}
});
} }
/**
* Moves the camera to a new position after some change (Zooming or Panning)
*/
private void repositionCamera() { private void repositionCamera() {
transforms.clear(); transforms.clear();
transforms.addAll( transforms.addAll(
new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0),
new Rotate(playerYacht.getHeadingProperty().getValue() + horizontalPan, new Rotate(playerBoat.getRotationProperty().getValue() + horizontalPan,
new Point3D(0, 0, 1)), new Point3D(0, 0, 1)),
new Rotate(60 + verticalPan, new Point3D(1, 0, 0)), new Rotate(60 + verticalPan, new Point3D(1, 0, 0)),
new Translate(0, 0, zoomFactor) new Translate(0, 0, zoomFactor)
); );
} }
/**
* Adjusts the zoom amount (camera depth) by some adjustment value
* @param adjustment the adjustment to be made to the camera
*/
private void adjustZoomFactor(Double adjustment) { private void adjustZoomFactor(Double adjustment) {
if (zoomFactor + adjustment < -15.0 && zoomFactor + adjustment > -125.0) { if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) {
zoomFactor = zoomFactor + adjustment; zoomFactor = zoomFactor + adjustment;
repositionCamera(); repositionCamera();
} }
} }
/**
* Adjusts the Vertical Panning of the Camera
* @param adjustment the adjustment to be made to the camera
*/
private void adjustVerticalPan(Double adjustment) { private void adjustVerticalPan(Double adjustment) {
if (verticalPan + adjustment >= -20 && verticalPan + adjustment <= 20) { if (verticalPan + adjustment >= -VERTICAL_PAN_LIMIT
&& verticalPan + adjustment <= VERTICAL_PAN_LIMIT) {
verticalPan += adjustment; verticalPan += adjustment;
repositionCamera(); repositionCamera();
} }
} }
/**
* Adjusts the Horizontal Panning of the Camera.
* @param adjustment the adjustment to be made to the camera
*/
private void adjustHorizontalPan(Double adjustment) {
this.horizontalPan += adjustment;
repositionCamera();
}
@Override @Override
public void zoomIn() { public void zoomIn() {
adjustZoomFactor(5.0); adjustZoomFactor(ZOOM_STEP);
} }
@Override @Override
public void zoomOut() { public void zoomOut() {
adjustZoomFactor(-5.0); adjustZoomFactor(-ZOOM_STEP);
} }
/*
These have been left intentionally empty for now. it would be cool to be able to pan around the boat and have the camera move around the boat though.
*/
@Override @Override
public void panLeft() { public void panLeft() {
this.horizontalPan -= 5; adjustHorizontalPan(-PAN_STEP);
repositionCamera();
} }
@Override @Override
public void panRight() { public void panRight() {
this.horizontalPan += 5; adjustHorizontalPan(PAN_STEP);
repositionCamera();
} }
@Override @Override
public void panUp() { public void panUp() {
adjustVerticalPan(-5.0); adjustVerticalPan(-PAN_STEP);
} }
@Override @Override
public void panDown() { public void panDown() {
adjustVerticalPan(5.0); adjustVerticalPan(PAN_STEP);
} }
} }
@@ -1,47 +1,113 @@
package seng302.visualiser.cameras; package seng302.visualiser.cameras;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.Point3D;
import javafx.scene.PerspectiveCamera; import javafx.scene.PerspectiveCamera;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Transform; import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate; import javafx.scene.transform.Translate;
public class IsometricCamera extends PerspectiveCamera implements RaceCamera { public class IsometricCamera extends PerspectiveCamera implements RaceCamera {
ObservableList<Transform> transforms; private final Double MIN_X = -120.0;
private final Double MAX_X = 125.0;
public IsometricCamera(Double cameraStartX, Double cameraStartY, Double cameraDepth) { private final Double MIN_Y = 40.0;
private final Double MAX_Y = 170.0;
private final Double PAN_LIMIT = 160.0;
private final Double NEAR_ZOOM_LIMIT = -50.0;
private final Double FAR_ZOOM_LIMIT = -160.0;
private Double horizontalPan;
private Double verticalPan;
private Double zoomFactor;
private ObservableList<Transform> transforms;
public IsometricCamera(Double cameraStartX, Double cameraStartY) {
super(true); super(true);
transforms = this.getTransforms(); transforms = this.getTransforms();
transforms.addAll(new Translate(cameraStartX, cameraStartY, cameraDepth));
zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0;
horizontalPan = cameraStartX;
verticalPan = cameraStartY;
updateCamera();
}
/**
* Moves the camera to a new position after some change (Zooming or Panning)
*/
private void updateCamera() {
transforms.clear();
transforms.addAll(
new Translate(horizontalPan, verticalPan, zoomFactor),
new Rotate(30, new Point3D(1, 0, 0))
);
}
/**
* Adjusts the zoom amount (camera depth) by some adjustment value
*
* @param adjustment the adjustment to be made to the camera
*/
private void adjustZoomFactor(Double adjustment) {
if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) {
zoomFactor = zoomFactor + adjustment;
updateCamera();
}
}
/**
* Adjusts the Vertical Panning of the Camera
* @param adjustment the adjustment to be made to the camera
*/
private void adjustVerticalPan(Double adjustment) {
if (verticalPan + adjustment >= MIN_Y && verticalPan + adjustment <= MAX_Y) {
verticalPan += adjustment;
updateCamera();
}
}
/**
* Adjusts the Horizontal Panning of the Camera.
* @param adjustment the adjustment to be made to the camera
*/
private void adjustHorizontalPan(Double adjustment) {
if (horizontalPan + adjustment >= MIN_X && horizontalPan + adjustment <= MIN_Y) {
this.horizontalPan += adjustment;
updateCamera();
}
} }
@Override @Override
public void zoomIn() { public void zoomIn() {
transforms.addAll(new Translate(0, 0, 1.5)); adjustZoomFactor(-2.5);
} }
@Override @Override
public void zoomOut() { public void zoomOut() {
transforms.addAll(new Translate(0, 0, -1.5)); adjustZoomFactor(2.5);
} }
@Override @Override
public void panLeft() { public void panLeft() {
transforms.addAll(new Translate(-1, 0, 0)); adjustHorizontalPan(-2.5);
} }
@Override @Override
public void panRight() { public void panRight() {
transforms.addAll(new Translate(1, 0, 0)); adjustHorizontalPan(2.5);
} }
@Override @Override
public void panUp() { public void panUp() {
transforms.addAll(new Translate(0, -1, 0)); adjustVerticalPan(-2.5);
} }
@Override @Override
public void panDown() { public void panDown() {
transforms.addAll(new Translate(0, 1, 0)); adjustVerticalPan(2.5);
} }
} }
@@ -1,8 +1,8 @@
package seng302.visualiser.cameras; package seng302.visualiser.cameras;
import javafx.beans.value.ChangeListener; import java.util.Arrays;
import javafx.beans.value.ObservableValue; import javafx.beans.property.DoubleProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.scene.PerspectiveCamera; import javafx.scene.PerspectiveCamera;
import javafx.scene.transform.Transform; import javafx.scene.transform.Transform;
@@ -11,75 +11,113 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject;
public class TopDownCamera extends PerspectiveCamera implements RaceCamera { public class TopDownCamera extends PerspectiveCamera implements RaceCamera {
private final Double PAN_LIMIT = 30.0;
private final Double NEAR_ZOOM_LIMIT = -30.0;
private final Double FAR_ZOOM_LIMIT = -130.0;
private final Double ZOOM_STEP = 2.5;
private ObservableList<Transform> transforms; private ObservableList<Transform> transforms;
private BoatObject playerBoat; private BoatObject playerBoat;
private Double zoomFactor;
private Double horizontalPan;
private Double verticalPan;
public TopDownCamera() { public TopDownCamera() {
super(true); super(true);
transforms = this.getTransforms(); transforms = this.getTransforms();
transforms.add(new Translate(0, 0, -125));
zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0;
horizontalPan = 0.0;
verticalPan = 0.0;
} }
/**
* Sets a player boat object to observe and update the camera with.
*
* @param playerBoat The player boat to be observed.
*/
public void setPlayerBoat(BoatObject playerBoat) { public void setPlayerBoat(BoatObject playerBoat) {
this.playerBoat = playerBoat; this.playerBoat = playerBoat;
this.playerBoat.layoutXProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
updateCameraX((Double) oldValue, (Double) newValue);
}
});
this.playerBoat.layoutYProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
updateCameraY((Double) oldValue, (Double) newValue);
}
});
}
for (DoubleProperty o : Arrays
private void updateCameraX(Double oldXValue, Double newXValue) { .asList(playerBoat.layoutXProperty(), playerBoat.layoutYProperty())) {
if (transforms.size() == 0) { // boat is placed and then moved at start, o.addListener((obs, oldVal, newVal) -> updateCamera());
transforms.addAll(
new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), -125)
);
} else {
transforms.addAll(new Translate(newXValue - oldXValue, 0, 0));
} }
} }
private void updateCameraY(Double oldYValue, Double newYValue) { /**
transforms.addAll(new Translate(0, (newYValue - oldYValue), 0)); * Moves the camera to a new position after some change (Zooming or Panning)
*/
private void updateCamera() {
transforms.clear();
transforms.addAll(
new Translate(playerBoat.getLayoutX() + horizontalPan,
playerBoat.getLayoutY() + verticalPan, zoomFactor)
);
}
/**
* Adjusts the zoom amount (camera depth) by some adjustment value
* @param adjustment the adjustment to be made to the camera
*/
private void adjustZoomFactor(Double adjustment) {
if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) {
zoomFactor = zoomFactor + adjustment;
updateCamera();
}
}
/**
* Adjusts the Vertical Panning of the Camera
* @param adjustment the adjustment to be made to the camera
*/
private void adjustVerticalPan(Double adjustment) {
if (verticalPan + adjustment >= -PAN_LIMIT && verticalPan + adjustment <= PAN_LIMIT) {
verticalPan += adjustment;
updateCamera();
}
}
/**
* Adjusts the Horizontal Panning of the Camera.
* @param adjustment the adjustment to be made to the camera
*/
private void adjustHorizontalPan(Double adjustment) {
if (horizontalPan + adjustment >= -PAN_LIMIT && horizontalPan + adjustment <= PAN_LIMIT) {
horizontalPan += adjustment;
updateCamera();
}
} }
@Override @Override
public void zoomIn() { public void zoomIn() {
transforms.addAll(new Translate(0, 0, 1.5)); adjustZoomFactor(ZOOM_STEP);
} }
@Override @Override
public void zoomOut() { public void zoomOut() {
transforms.addAll(new Translate(0, 0, -1.5)); adjustZoomFactor(-ZOOM_STEP);
} }
@Override @Override
public void panLeft() { public void panLeft() {
transforms.addAll(new Translate(-1, 0, 0)); adjustHorizontalPan(-1.0);
} }
@Override @Override
public void panRight() { public void panRight() {
transforms.addAll(new Translate(1, 0, 0)); adjustHorizontalPan(1.0);
} }
@Override @Override
public void panUp() { public void panUp() {
transforms.addAll(new Translate(0, -1, 0)); adjustVerticalPan(-1.0);
} }
@Override @Override
public void panDown() { public void panDown() {
transforms.addAll(new Translate(0, 1, 0)); adjustVerticalPan(1.0);
} }
} }
@@ -44,12 +44,14 @@ import javafx.scene.shape.Polyline;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import javax.swing.ImageIcon;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.ClientYacht.PowerUpListener; import seng302.model.ClientYacht.PowerUpListener;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.Token;
import seng302.model.token.TokenType; import seng302.model.token.TokenType;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.GameView3D; import seng302.visualiser.GameView3D;
@@ -115,7 +117,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML @FXML
private Label positionLabel, boatSpeedLabel, boatHeadingLabel; private Label positionLabel, boatSpeedLabel, boatHeadingLabel;
@FXML @FXML
private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon; private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon;
//Race Data //Race Data
private Map<Integer, ClientYacht> participants; private Map<Integer, ClientYacht> participants;
@@ -136,6 +138,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private JFXDialog finishScreenDialog; private JFXDialog finishScreenDialog;
private FinishDialogController finishDialogController; private FinishDialogController finishDialogController;
//Icon stuff
private Timer blinkingTimer = new Timer();
private ImageView iconToDisplay;
public void initialize() { public void initialize() {
Sounds.stopMusic(); Sounds.stopMusic();
Sounds.playRaceMusic(); Sounds.playRaceMusic();
@@ -213,6 +219,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}); });
player.addPowerUpListener(this::displayPowerUpIcon); player.addPowerUpListener(this::displayPowerUpIcon);
player.addPowerDownListener(this::removeIcon);
updateOrder(raceState.getPlayerPositions()); updateOrder(raceState.getPlayerPositions());
gameView = new GameView3D(); gameView = new GameView3D();
@@ -257,7 +264,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
*/ */
private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) { private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) {
if (yacht == player) { if (yacht == player) {
final ImageView iconToDisplay; if (iconToDisplay != null) {
iconToDisplay.setVisible(false);
}
switch (tokenType) { switch (tokenType) {
case BOOST: case BOOST:
@@ -272,6 +281,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
case BUMPER: case BUMPER:
iconToDisplay = bumperIcon; iconToDisplay = bumperIcon;
break; break;
case RANDOM:
iconToDisplay = badRandomIcon;
break;
default: default:
iconToDisplay = velocityIcon; iconToDisplay = velocityIcon;
} }
@@ -280,7 +292,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
iconToDisplay.setVisible(true); iconToDisplay.setVisible(true);
//Start blinking icon towards end //Start blinking icon towards end
Timer blinkingTimer = new Timer(); if (blinkingTimer != null) {
blinkingTimer.cancel();
}
blinkingTimer = new Timer("Blinking Timer");
blinkingTimer.schedule(new TimerTask() { blinkingTimer.schedule(new TimerTask() {
Boolean isVisible = true; Boolean isVisible = true;
@@ -290,16 +305,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
iconToDisplay.setVisible(isVisible); iconToDisplay.setVisible(isVisible);
} }
}, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD); }, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD);
}
}
//Turn icon off after the time out public void removeIcon(ClientYacht yacht) {
Timer switchOffTimer = new Timer(); if (yacht == player) {
switchOffTimer.schedule(new TimerTask() { blinkingTimer.cancel();
@Override iconToDisplay.setVisible(false);
public void run() { iconToDisplay = null;
blinkingTimer.cancel();
iconToDisplay.setVisible(false);
}
}, tokenType.getTimeout());
} }
} }
@@ -5,6 +5,12 @@ import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialog.DialogTransition; import com.jfoenix.controls.JFXDialog.DialogTransition;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator; import com.jfoenix.validation.RequiredFieldValidator;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
@@ -32,6 +38,10 @@ import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import seng302.visualiser.controllers.dialogs.ServerCreationController;
import seng302.visualiser.validators.HostNameFieldValidator;
import seng302.visualiser.validators.NumberRangeValidator;
import seng302.visualiser.validators.ValidationTools;
public class ServerListController implements Initializable, ServerListenerDelegate { public class ServerListController implements Initializable, ServerListenerDelegate {
@@ -63,6 +73,14 @@ public class ServerListController implements Initializable, ServerListenerDelega
private Logger logger = LoggerFactory.getLogger(ServerListController.class); private Logger logger = LoggerFactory.getLogger(ServerListController.class);
private JFXDialog directConnectDialog; private JFXDialog directConnectDialog;
private JFXDialog serverCreationDialog;
private List<ServerCreationDialogListener> serverCreationDialogListeners = new ArrayList<>();
@FunctionalInterface
public interface ServerCreationDialogListener {
void notifyClosure();
}
// TODO: 12/09/17 ajm412: break this method down, its way too long. // TODO: 12/09/17 ajm412: break this method down, its way too long.
@Override @Override
@@ -166,6 +184,8 @@ public class ServerListController implements Initializable, ServerListenerDelega
serverListHostButton.setOnAction(action -> { serverListHostButton.setOnAction(action -> {
showServerCreationDialog(); showServerCreationDialog();
}); });
addServerCreationDialogListener(this::closeServerCreationDialog);
} }
/** /**
@@ -176,9 +196,11 @@ public class ServerListController implements Initializable, ServerListenerDelega
FXMLLoader dialogContent = new FXMLLoader(getClass().getResource( FXMLLoader dialogContent = new FXMLLoader(getClass().getResource(
"/views/dialogs/ServerCreationDialog.fxml")); "/views/dialogs/ServerCreationDialog.fxml"));
try { try {
JFXDialog dialog = new JFXDialog(serverListMainStackPane, dialogContent.load(), serverCreationDialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
DialogTransition.CENTER); DialogTransition.CENTER);
dialog.show(); ServerCreationController serverCreationController = dialogContent.getController();
serverCreationController.setListener(serverCreationDialogListeners);
serverCreationDialog.show();
Sounds.playButtonClick(); Sounds.playButtonClick();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -205,6 +227,10 @@ public class ServerListController implements Initializable, ServerListenerDelega
return dcDialog; return dcDialog;
} }
private void closeServerCreationDialog() {
serverCreationDialog.close();
}
/** /**
* Validates the connection and attempts to connect to a given hostname and port number. * Validates the connection and attempts to connect to a given hostname and port number.
*/ */
@@ -303,4 +329,14 @@ public class ServerListController implements Initializable, ServerListenerDelega
public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) { public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) {
Platform.runLater(() -> refreshServers(servers)); Platform.runLater(() -> refreshServers(servers));
} }
private void addServerCreationDialogListener(
ServerCreationDialogListener serverCreationDialogListener) {
serverCreationDialogListeners.add(serverCreationDialogListener);
}
private void removeServerCreationDialogListener(
ServerCreationDialogListener serverCreationDialogListener) {
serverCreationDialogListeners.remove(serverCreationDialogListener);
}
} }
@@ -21,6 +21,7 @@ import seng302.gameServer.ServerAdvertiser;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.GameClient; import seng302.visualiser.GameClient;
import seng302.visualiser.controllers.dialogs.KeyBindingDialogController; import seng302.visualiser.controllers.dialogs.KeyBindingDialogController;
import seng302.visualiser.controllers.dialogs.PopupDialogController;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
@@ -97,8 +98,6 @@ public class ViewManager {
gameClient.stopGame(); gameClient.stopGame();
System.exit(0); System.exit(0);
}); });
jfxSnackbar = new JFXSnackbar(decorator);
} }
/** /**
@@ -183,6 +182,7 @@ public class ViewManager {
} }
}); });
jfxSnackbar = new JFXSnackbar(decorator);
} }
/** /**
@@ -216,6 +216,7 @@ public class ViewManager {
.getController(); .getController();
keyBindingDialogController.setGameClient(this.gameClient); keyBindingDialogController.setGameClient(this.gameClient);
keyBindingDialog.show(); keyBindingDialog.show();
decorator.requestFocus();
Sounds.playButtonClick(); Sounds.playButtonClick();
} }
} }
@@ -225,6 +226,26 @@ public class ViewManager {
keyBindingDialog.close(); keyBindingDialog.close();
} }
public PopupDialogController showPopupDialog() {
FXMLLoader dialogContent = new FXMLLoader(
getClass().getResource("/views/dialogs/PopupDialog.fxml"));
for (Node node : decorator.getChildren()) {
if (node instanceof StackPane) {
try {
JFXDialog dialog = new JFXDialog((StackPane) node, dialogContent.load(),
DialogTransition.CENTER);
PopupDialogController popupDialogController = dialogContent.getController();
popupDialogController.setPopupDialog(dialog);
dialog.show();
return popupDialogController;
} catch (IOException e) {
logger.error("Cannot load Popup dialog");
}
}
}
return null;
}
/** /**
* Show a snackbar at the bottom of the app for 1 second. * Show a snackbar at the bottom of the app for 1 second.
* *
@@ -48,6 +48,16 @@ public class KeyBindingDialogController implements Initializable {
private Label downwindLabel; private Label downwindLabel;
@FXML @FXML
private JFXToggleButton turningToggle; private JFXToggleButton turningToggle;
@FXML
private JFXButton viewButton;
@FXML
private JFXButton rightButton;
@FXML
private JFXButton leftButton;
@FXML
private JFXButton forwardButton;
@FXML
private JFXButton backwardButton;
//---------FXML END---------// //---------FXML END---------//
private GameKeyBind gameKeyBind; private GameKeyBind gameKeyBind;
@@ -60,7 +70,8 @@ public class KeyBindingDialogController implements Initializable {
gameKeyBind = GameKeyBind.getInstance(); gameKeyBind = GameKeyBind.getInstance();
buttons = new ArrayList<>(); buttons = new ArrayList<>();
Collections.addAll(buttons, Collections.addAll(buttons,
zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn); zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn,
viewButton, rightButton, leftButton, forwardButton, backwardButton);
bindButtonWithAction(); bindButtonWithAction();
loadKeyBind(); loadKeyBind();
@@ -76,12 +87,10 @@ public class KeyBindingDialogController implements Initializable {
resetBtn.setOnMouseClicked(event -> { resetBtn.setOnMouseClicked(event -> {
gameKeyBind.setToDefault(); gameKeyBind.setToDefault();
loadKeyBind(); loadKeyBind();
showSnackBar("All keys reset!", false);
}); });
closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog()); closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog());
keyBindingDialogHeader.setFocusTraversable(true);
keyBindingDialogHeader.requestFocus();
} }
/** /**
@@ -106,7 +115,7 @@ public class KeyBindingDialogController implements Initializable {
*/ */
private void bindButtonWithAction() { private void bindButtonWithAction() {
buttonActionMap = new HashMap<>(); buttonActionMap = new HashMap<>();
for (int i = 0; i < 7; i++) { for (int i = 0; i < 12; i++) {
buttonActionMap.put(buttons.get(i), KeyAction.getType(i + 1)); buttonActionMap.put(buttons.get(i), KeyAction.getType(i + 1));
} }
} }
@@ -149,6 +158,7 @@ public class KeyBindingDialogController implements Initializable {
+ "-fx-background-color: -fx-pp-front-color; " + "-fx-background-color: -fx-pp-front-color; "
+ "-fx-text-fill: -fx-pp-theme-color; " + "-fx-text-fill: -fx-pp-theme-color; "
+ "-fx-font-size: 13;"); + "-fx-font-size: 13;");
keyBindingDialogHeader.requestFocus();
} }
/** /**
@@ -0,0 +1,56 @@
package seng302.visualiser.controllers.dialogs;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
public class PopupDialogController implements Initializable {
@FXML
private Label headerLabel;
@FXML
private Label contentLabel;
@FXML
private Label closeLabel;
@FXML
private JFXButton optionButton;
@FXML
private JFXDialog popupDialog;
@Override
public void initialize(URL location, ResourceBundle resources) {
}
public void setContent(String content) {
this.contentLabel.setText(content);
}
public void setHeader(String header) {
this.headerLabel.setText(header);
}
public void setOptionButton(JFXButton jfxButton) {
this.optionButton = jfxButton;
}
public void setOptionButtonText(String text) {
this.optionButton.setText(text);
}
public void setOptionButtonEventHandler(EventHandler<? super MouseEvent> eventHandler) {
this.optionButton.setOnMouseClicked(eventHandler);
}
public void setPopupDialog(JFXDialog popupDialog) {
this.popupDialog = popupDialog;
this.closeLabel.setOnMouseClicked(event -> this.popupDialog.close());
}
}
@@ -6,6 +6,7 @@ import com.jfoenix.controls.JFXSlider;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.RequiredFieldValidator; import com.jfoenix.validation.RequiredFieldValidator;
import java.net.URL; import java.net.URL;
import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
@@ -15,6 +16,7 @@ import javafx.scene.layout.AnchorPane;
import seng302.gameServer.ServerDescription; import seng302.gameServer.ServerDescription;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.MapMaker; import seng302.visualiser.MapMaker;
import seng302.visualiser.controllers.ServerListController.ServerCreationDialogListener;
import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.controllers.ViewManager;
import seng302.visualiser.validators.FieldLengthValidator; import seng302.visualiser.validators.FieldLengthValidator;
import seng302.visualiser.validators.ValidationTools; import seng302.visualiser.validators.ValidationTools;
@@ -31,6 +33,8 @@ public class ServerCreationController implements Initializable {
@FXML @FXML
private JFXButton submitBtn; private JFXButton submitBtn;
@FXML @FXML
private Label closeLabel;
@FXML
private JFXButton nextMapButton; private JFXButton nextMapButton;
@FXML @FXML
private JFXButton lastMapButton; private JFXButton lastMapButton;
@@ -49,6 +53,8 @@ public class ServerCreationController implements Initializable {
//---------FXML END---------// //---------FXML END---------//
private List<ServerCreationDialogListener> serverCreationDialogListeners;
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
legsSlider.setMax(10); legsSlider.setMax(10);
legsSlider.setValue(4); legsSlider.setValue(4);
@@ -86,6 +92,7 @@ public class ServerCreationController implements Initializable {
mapHolder.getChildren().setAll(mapMaker.getCurrentGameView()); mapHolder.getChildren().setAll(mapMaker.getCurrentGameView());
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName()); mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
closeLabel.setOnMouseClicked(event -> notifyListeners());
} }
/** /**
@@ -144,4 +151,14 @@ public class ServerCreationController implements Initializable {
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName()); mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
} }
public void setListener(List<ServerCreationDialogListener> serverCreationDialogListeners) {
this.serverCreationDialogListeners = serverCreationDialogListeners;
}
public void notifyListeners() {
for (ServerCreationDialogListener serverCreationDialogListener : serverCreationDialogListeners) {
serverCreationDialogListener.notifyClosure();
}
}
} }
@@ -3,6 +3,7 @@ package seng302.visualiser.fxObjects.assets_3D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.geometry.Point3D; import javafx.geometry.Point3D;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@@ -30,12 +31,15 @@ public class BoatObject extends Group {
private Boolean isSelected = false; private Boolean isSelected = false;
private Rotate rotation = new Rotate(0, new Point3D(0,0,1)); private Rotate rotation = new Rotate(0, new Point3D(0,0,1));
private ReadOnlyDoubleWrapper rotationProperty;
private List<SelectedBoatListener> selectedBoatListenerListeners = new ArrayList<>(); private List<SelectedBoatListener> selectedBoatListenerListeners = new ArrayList<>();
/** /**
* Creates a BoatGroup with the default triangular boat polygon. * Creates a BoatGroup with the default triangular boat polygon.
*/ */
public BoatObject(BoatMeshType boatMeshType) { public BoatObject(BoatMeshType boatMeshType) {
rotationProperty = new ReadOnlyDoubleWrapper(0.0);
boatAssets = ModelFactory.boatGameView(boatMeshType, colour); boatAssets = ModelFactory.boatGameView(boatMeshType, colour);
boatAssets.hideSail(); boatAssets.hideSail();
boatAssets.getAssets().getTransforms().addAll( boatAssets.getAssets().getTransforms().addAll(
@@ -83,6 +87,7 @@ public class BoatObject extends Group {
private void rotateTo(double heading, boolean sailsIn, double windDir) { private void rotateTo(double heading, boolean sailsIn, double windDir) {
rotationProperty.set(heading);
rotation.setAngle(heading); rotation.setAngle(heading);
wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1))); wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1)));
if (sailsIn) { if (sailsIn) {
@@ -130,4 +135,8 @@ public class BoatObject extends Group {
public void addSelectedBoatListener(SelectedBoatListener sbl) { public void addSelectedBoatListener(SelectedBoatListener sbl) {
selectedBoatListenerListeners.add(sbl); selectedBoatListenerListeners.add(sbl);
} }
public ReadOnlyDoubleWrapper getRotationProperty() {
return rotationProperty;
}
} }
+5
View File
@@ -51,6 +51,11 @@
/********* customised scroll bar for scroll pane ***********/ /********* customised scroll bar for scroll pane ***********/
.scroll-pane {
-fx-focus-traversable: false;
-fx-border-style: none;
}
/* The main scrollbar **track** CSS class */ /* The main scrollbar **track** CSS class */
.scroll-bar:horizontal .track, .scroll-bar:horizontal .track,
.scroll-bar:vertical .track { .scroll-bar:vertical .track {
@@ -9,8 +9,13 @@
} }
#closeLabel:hover { #closeLabel:hover {
-fx-text-fill: -fx-pp-theme-color; -fx-text-fill: red;
-fx-font-size: 33; -fx-font-size: 33px;
}
.sectionLabel {
-fx-text-fill: -fx-pp-dark-text-color;
-fx-font-size: 20px;
} }
JFXButton { JFXButton {
+33
View File
@@ -0,0 +1,33 @@
#headerLabel {
-fx-font-size: 20px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel {
-fx-font-size: 22px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel:hover {
-fx-font-size: 24px;
-fx-text-fill: red;
}
#contentLabel {
-fx-font-size: 22px;
-fx-text-fill: -fx-pp-dark-text-color;
}
#optionButton {
-fx-background-color: -fx-pp-theme-color;
-fx-text-fill: -fx-pp-light-text-color;
-fx-font-size: 18px;
-fx-effect: -fx-pp-dropshadow-light;
-fx-max-height: 55;
-fx-focus-traversable: false;
}
#optionButton:hover {
-fx-font-size: 20px !important;
-fx-background-color: -fx-pp-light-theme-color;
}
@@ -45,3 +45,13 @@
.maxPlayers { .maxPlayers {
-fx-font-size: 13px; -fx-font-size: 13px;
} }
#closeLabel {
-fx-font-size: 30;
-fx-text-fill: -fx-pp-dark-text-color;
}
#closeLabel:hover {
-fx-text-fill: red;
-fx-font-size: 33px;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

+7 -7
View File
@@ -5,8 +5,8 @@
<author>Blender User</author> <author>Blender User</author>
<authoring_tool>Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22</authoring_tool> <authoring_tool>Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22</authoring_tool>
</contributor> </contributor>
<created>2017-09-19T15:45:46</created> <created>2017-09-26T19:13:35</created>
<modified>2017-09-19T15:45:46</modified> <modified>2017-09-26T19:13:35</modified>
<unit name="meter" meter="1"/> <unit name="meter" meter="1"/>
<up_axis>Z_UP</up_axis> <up_axis>Z_UP</up_axis>
</asset> </asset>
@@ -23,10 +23,10 @@
<color sid="ambient">0 0 0 1</color> <color sid="ambient">0 0 0 1</color>
</ambient> </ambient>
<diffuse> <diffuse>
<color sid="diffuse">0.004555753 0.0885511 0.003947978 1</color> <color sid="diffuse">0.01630632 0.52949 0.0134405 1</color>
</diffuse> </diffuse>
<specular> <specular>
<color sid="specular">0.25 0.25 0.25 1</color> <color sid="specular">0.125 0.125 0.125 1</color>
</specular> </specular>
<shininess> <shininess>
<float sid="shininess">50</float> <float sid="shininess">50</float>
@@ -49,10 +49,10 @@
<color sid="ambient">0 0 0 1</color> <color sid="ambient">0 0 0 1</color>
</ambient> </ambient>
<diffuse> <diffuse>
<color sid="diffuse">0.64 0.1458963 0.001825521 1</color> <color sid="diffuse">0.64 0.5334254 0 1</color>
</diffuse> </diffuse>
<specular> <specular>
<color sid="specular">0.25 0.25 0.25 1</color> <color sid="specular">0.125 0.125 0.125 1</color>
</specular> </specular>
<shininess> <shininess>
<float sid="shininess">50</float> <float sid="shininess">50</float>
@@ -87,7 +87,7 @@
</technique_common> </technique_common>
</source> </source>
<source id="Icosphere_001-mesh-normals"> <source id="Icosphere_001-mesh-normals">
<float_array id="Icosphere_001-mesh-normals-array" count="240">0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616991 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.1875942 0.5773453 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524</float_array> <float_array id="Icosphere_001-mesh-normals-array" count="240">0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284252 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616992 0.7240421 0.1947362 0.6616954 0.4911194 0.356821 0.7946576 0.4089462 0.6284253 0.6616984 -0.1875943 0.5773454 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875943 -0.5773454 0.7946577 0.03853034 -0.7487788 0.6616992 0.4911193 -0.356821 0.7946577 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875943 0.5773454 -0.7946577 -0.0385304 0.7487789 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911193 0.356821 -0.7946577 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911194 -0.356821 -0.7946576 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487789 -0.6616991 0.1875943 -0.5773454 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150898 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150899 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524</float_array>
<technique_common> <technique_common>
<accessor source="#Icosphere_001-mesh-normals-array" count="80" stride="3"> <accessor source="#Icosphere_001-mesh-normals-array" count="80" stride="3">
<param name="X" type="float"/> <param name="X" type="float"/>
+70 -60
View File
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<?import com.jfoenix.controls.*?> <?import com.jfoenix.controls.*?>
<?import java.lang.*?> <?import java.lang.*?>
<?import javafx.geometry.*?> <?import javafx.geometry.*?>
@@ -7,11 +9,23 @@
<?import javafx.scene.image.*?> <?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?> <?import javafx.scene.text.*?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXTextField?>
<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308" <StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308"
maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0"
prefHeight="800.0" prefWidth="1200.0" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8.0.111"
style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.RaceViewController"> fx:controller="seng302.visualiser.controllers.RaceViewController">
<children> <children>
@@ -32,17 +46,15 @@
valignment="BOTTOM" vgrow="SOMETIMES"/> valignment="BOTTOM" vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<GridPane id="timerGrid" fx:id="timerGrid" prefWidth="192.0" <GridPane id="timerGrid" fx:id="timerGrid" prefWidth="192.0" styleClass="timer">
styleClass="timer">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="50.0" minWidth="50.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="50.0" minWidth="50.0"
prefWidth="50.0"/> prefWidth="50.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="135.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="135.0" minWidth="135.0"
minWidth="135.0" prefWidth="135.0"/> prefWidth="135.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets/>
@@ -74,25 +86,20 @@
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="2"> <GridPane GridPane.columnIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0"
prefWidth="100.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
</GridPane> </GridPane>
<GridPane fx:id="chatGridPane" GridPane.columnIndex="2" <GridPane fx:id="chatGridPane" GridPane.columnIndex="2" GridPane.rowIndex="2">
GridPane.rowIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="390.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="390.0" minWidth="390.0"
minWidth="390.0" prefWidth="390.0"/> prefWidth="390.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="1.7976931348623157E308" <RowConstraints maxHeight="1.7976931348623157E308" vgrow="SOMETIMES"/>
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" <RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0"
vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
@@ -115,22 +122,21 @@
minWidth="90.0" prefWidth="90.0"/> minWidth="90.0" prefWidth="90.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="50.0" minHeight="50.0" <RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0"
prefHeight="50.0" valignment="CENTER" vgrow="SOMETIMES"/> valignment="CENTER" vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<JFXButton fx:id="chatSend" alignment="CENTER" <JFXButton fx:id="chatSend" alignment="CENTER" buttonType="RAISED"
buttonType="RAISED" maxHeight="-Infinity" focusTraversable="false" maxHeight="-Infinity"
maxWidth="1.7976931348623157E308" minHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="35.0" text="SEND" minWidth="-Infinity" prefHeight="35.0" text="SEND"
GridPane.columnIndex="1"> GridPane.columnIndex="1">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
top="10.0"/>
</GridPane.margin> </GridPane.margin>
</JFXButton> </JFXButton>
<JFXTextField fx:id="chatInput" maxHeight="35.0" <JFXTextField fx:id="chatInput" focusTraversable="false"
minHeight="-Infinity" prefHeight="35.0"> maxHeight="35.0" minHeight="-Infinity" prefHeight="35.0">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="20.0" right="10.0"/> <Insets bottom="10.0" left="20.0" right="10.0"/>
</GridPane.margin> </GridPane.margin>
@@ -152,28 +158,27 @@
prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER" prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER"
GridPane.rowIndex="2" GridPane.valignment="BOTTOM"> GridPane.rowIndex="2" GridPane.valignment="BOTTOM">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="110.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="110.0" minWidth="110.0"
minWidth="110.0" prefWidth="110.0"/> prefWidth="110.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="132.0" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="132.0" minWidth="10.0"
prefWidth="132.0"/> prefWidth="132.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="120.0" minHeight="120.0" <RowConstraints maxHeight="120.0" minHeight="120.0" prefHeight="120.0"
prefHeight="120.0" vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" <RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<Label fx:id="positionLabel" text="Position:" <Label fx:id="positionLabel" text="Position:" GridPane.columnIndex="1"
GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.halignment="LEFT" GridPane.rowSpan="2" GridPane.valignment="TOP">
GridPane.rowSpan="2" GridPane.valignment="TOP">
<padding> <padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/>
</padding> </padding>
</Label> </Label>
<Label fx:id="boatSpeedLabel" text="Boat Speed:" <Label fx:id="boatSpeedLabel" text="Boat Speed:" GridPane.columnIndex="1"
GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.halignment="LEFT" GridPane.rowSpan="2"
GridPane.rowSpan="2" GridPane.valignment="CENTER"> GridPane.valignment="CENTER">
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets/>
</opaqueInsets> </opaqueInsets>
@@ -182,8 +187,8 @@
</padding> </padding>
</Label> </Label>
<Label fx:id="boatHeadingLabel" text="Boat Heading:" <Label fx:id="boatHeadingLabel" text="Boat Heading:"
GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2"
GridPane.rowSpan="2" GridPane.valignment="BOTTOM"> GridPane.valignment="BOTTOM">
<padding> <padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/>
</padding> </padding>
@@ -196,12 +201,12 @@
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="120.0" minHeight="120.0" <RowConstraints maxHeight="120.0" minHeight="120.0"
prefHeight="120.0" vgrow="SOMETIMES"/> prefHeight="120.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="30.0" minHeight="30.0" <RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
prefHeight="30.0" vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<ImageView fx:id="windImageView" fitHeight="92.0" <ImageView fx:id="windImageView" fitHeight="92.0" fitWidth="109.0"
fitWidth="109.0" pickOnBounds="true" preserveRatio="true" pickOnBounds="true" preserveRatio="true"
GridPane.halignment="CENTER" GridPane.rowSpan="2" GridPane.halignment="CENTER" GridPane.rowSpan="2"
GridPane.valignment="CENTER"/> GridPane.valignment="CENTER"/>
<Label fx:id="windSpeedLabel" text="0.0 Knots" <Label fx:id="windSpeedLabel" text="0.0 Knots"
@@ -230,30 +235,27 @@
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="1" GridPane.rowIndex="2"> <GridPane GridPane.columnIndex="1" GridPane.rowIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0"
prefWidth="100.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" <RowConstraints maxHeight="152.0" minHeight="10.0" prefHeight="152.0"
vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" <RowConstraints maxHeight="118.0" minHeight="10.0" prefHeight="98.0"
vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<ImageView fx:id="velocityIcon" fitHeight="123.0" fitWidth="139.0" <ImageView fx:id="velocityIcon" fitHeight="88.0" fitWidth="106.0"
pickOnBounds="true" preserveRatio="true" visible="false" pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.halignment="CENTER" GridPane.rowIndex="1"> GridPane.halignment="CENTER" GridPane.rowIndex="1">
<image> <image>
<Image url="@../icons/velocity.png"/> <Image url="@../icons/velocity.png"/>
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="handlingIcon" fitHeight="123.0" fitWidth="139.0" <ImageView fx:id="handlingIcon" fitHeight="87.0" fitWidth="98.0"
pickOnBounds="true" preserveRatio="true" visible="false" pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="1"> GridPane.rowIndex="1">
@@ -261,7 +263,7 @@
<Image url="@../icons/handlingIcon.png"/> <Image url="@../icons/handlingIcon.png"/>
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="windWalkerIcon" fitHeight="123.0" fitWidth="139.0" <ImageView fx:id="windWalkerIcon" fitHeight="83.0" fitWidth="100.0"
pickOnBounds="true" preserveRatio="true" visible="false" pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.columnIndex="2" GridPane.halignment="CENTER"
GridPane.rowIndex="1"> GridPane.rowIndex="1">
@@ -269,7 +271,7 @@
<Image url="@../icons/windWalkerIcon.png"/> <Image url="@../icons/windWalkerIcon.png"/>
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="bumperIcon" fitHeight="123.0" fitWidth="139.0" <ImageView fx:id="bumperIcon" fitHeight="83.0" fitWidth="88.0"
pickOnBounds="true" preserveRatio="true" visible="false" pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.columnIndex="3" GridPane.halignment="CENTER"
GridPane.rowIndex="1"> GridPane.rowIndex="1">
@@ -277,6 +279,14 @@
<Image url="@../icons/bumperIcon.png"/> <Image url="@../icons/bumperIcon.png"/>
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="badRandomIcon" fitHeight="69.0" fitWidth="103.0"
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="4" GridPane.halignment="CENTER"
GridPane.rowIndex="1" GridPane.valignment="CENTER">
<image>
<Image url="@../icons/slowedIcon.png"/>
</image>
</ImageView>
</children> </children>
</GridPane> </GridPane>
</children> </children>
@@ -286,4 +296,4 @@
<String fx:value="/css/Master.css"/> <String fx:value="/css/Master.css"/>
<String fx:value="/css/RaceView.css"/> <String fx:value="/css/RaceView.css"/>
</stylesheets> </stylesheets>
</StackPane> </StackPane>
@@ -6,117 +6,226 @@
<?import java.net.URL?> <?import java.net.URL?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<JFXDialogLayout fx:id="keyBindDialog" maxHeight="-Infinity" maxWidth="-Infinity" <JFXDialogLayout fx:id="keyBindDialog" maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity" prefHeight="580.0" prefWidth="500.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="580.0" prefWidth="500.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.dialogs.KeyBindingDialogController"> fx:controller="seng302.visualiser.controllers.dialogs.KeyBindingDialogController">
<stylesheets>
<URL value="@../../css/dialogs/KeyBindingDialog.css"/>
<URL value="@../../css/Master.css"/>
</stylesheets>
<children> <children>
<GridPane> <GridPane>
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="60.0"
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/> <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="60.0"
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="70.0"
vgrow="SOMETIMES"/> vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<Label fx:id="keyBindingDialogHeader" text="CUSTOM KEYBIND" GridPane.columnSpan="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" /> <ScrollPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1">
<Label text="ZOOM IN" GridPane.halignment="CENTER" GridPane.rowIndex="1" <content>
GridPane.valignment="CENTER"> <StackPane prefHeight="150.0" prefWidth="200.0">
<GridPane.margin> <children>
<Insets/> <GridPane>
</GridPane.margin></Label> <children>
<Label text="ZOOM OUT" GridPane.halignment="CENTER" GridPane.rowIndex="2" <JFXButton fx:id="viewButton" buttonType="RAISED"
GridPane.valignment="CENTER"> minHeight="35.0" prefWidth="120.0" text="F1"
<GridPane.margin> GridPane.columnIndex="1" GridPane.halignment="CENTER"
<Insets/> GridPane.rowIndex="14" GridPane.valignment="CENTER"/>
</GridPane.margin></Label> <Label text="VIEW ASPECT" GridPane.halignment="CENTER"
<Label text="VMG" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.rowIndex="14" GridPane.valignment="CENTER"/>
GridPane.valignment="CENTER"> <JFXButton fx:id="rightButton" buttonType="RAISED"
<GridPane.margin> minHeight="35.0" prefWidth="120.0" text="D"
<Insets/> GridPane.columnIndex="1" GridPane.halignment="CENTER"
</GridPane.margin></Label> GridPane.rowIndex="13" GridPane.valignment="CENTER"/>
<Label text="SAILS IN/OUT" GridPane.halignment="CENTER" GridPane.rowIndex="4" <Label text="RIGHT" GridPane.halignment="CENTER"
GridPane.valignment="CENTER"> GridPane.rowIndex="13" GridPane.valignment="CENTER"/>
<GridPane.margin> <JFXButton fx:id="leftButton" buttonType="RAISED"
<Insets/> minHeight="35.0" prefWidth="120.0" text="A"
</GridPane.margin></Label> GridPane.columnIndex="1" GridPane.halignment="CENTER"
<Label text="TACK/GYBE" GridPane.halignment="CENTER" GridPane.rowIndex="5" GridPane.rowIndex="12" GridPane.valignment="CENTER"/>
GridPane.valignment="CENTER"> <Label text="LEFT" GridPane.halignment="CENTER"
<GridPane.margin> GridPane.rowIndex="12" GridPane.valignment="CENTER"/>
<Insets/> <JFXButton fx:id="forwardButton" buttonType="RAISED"
</GridPane.margin></Label> minHeight="35.0" prefWidth="120.0" text="W"
<Label fx:id="upwindLabel" text="UPWIND" GridPane.halignment="CENTER" GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="6" GridPane.valignment="CENTER"> GridPane.rowIndex="10" GridPane.valignment="CENTER"/>
<GridPane.margin> <Label text="FORWARD" GridPane.halignment="CENTER"
<Insets/> GridPane.rowIndex="10" GridPane.valignment="CENTER"/>
</GridPane.margin></Label> <JFXButton fx:id="backwardButton" buttonType="RAISED"
<Label fx:id="downwindLabel" text="DOWNWIND" GridPane.halignment="CENTER" minHeight="35.0" prefWidth="120.0" text="S"
GridPane.rowIndex="7" GridPane.valignment="CENTER"> GridPane.columnIndex="1" GridPane.halignment="CENTER"
<GridPane.margin> GridPane.rowIndex="11" GridPane.valignment="CENTER"/>
<Insets/> <Label text="BACKWARD" GridPane.halignment="CENTER"
</GridPane.margin></Label> GridPane.rowIndex="11" GridPane.valignment="CENTER"/>
<JFXButton id="ZOOM IN" fx:id="zoomInbtn" buttonType="RAISED" maxHeight="-Infinity" <Label text="ZOOM IN" GridPane.halignment="CENTER"
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" text="Z" GridPane.rowIndex="1" GridPane.valignment="CENTER">
GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" <GridPane.margin>
GridPane.valignment="CENTER"/> <Insets/>
<JFXButton id="ZOOM OUT" fx:id="zoomOutBtn" buttonType="RAISED" maxHeight="-Infinity" </GridPane.margin>
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" text="X" </Label>
GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2" <Label text="ZOOM OUT" GridPane.halignment="CENTER"
GridPane.valignment="CENTER"/> GridPane.rowIndex="2" GridPane.valignment="CENTER">
<JFXButton id="VMG" fx:id="vmgBtn" buttonType="RAISED" maxHeight="-Infinity" <GridPane.margin>
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" <Insets/>
text="SPACE" GridPane.columnIndex="1" GridPane.halignment="CENTER" </GridPane.margin>
GridPane.rowIndex="3" GridPane.valignment="CENTER"/> </Label>
<JFXButton id="SAILS IN/OUT" fx:id="sailInOutBtn" buttonType="RAISED" <Label text="VMG" GridPane.halignment="CENTER"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" GridPane.rowIndex="3" GridPane.valignment="CENTER">
prefWidth="120.0" text="SHIFT" GridPane.columnIndex="1" GridPane.halignment="CENTER" <GridPane.margin>
GridPane.rowIndex="4" GridPane.valignment="CENTER"/> <Insets/>
<JFXButton id="TACK/GYBE" fx:id="tackGybeBtn" buttonType="RAISED" maxHeight="-Infinity" </GridPane.margin>
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" </Label>
text="ENTER" GridPane.columnIndex="1" GridPane.halignment="CENTER" <Label text="SAILS IN/OUT" GridPane.halignment="CENTER"
GridPane.rowIndex="5" GridPane.valignment="CENTER"/> GridPane.rowIndex="4" GridPane.valignment="CENTER">
<JFXButton id="UPWIND" fx:id="upwindBtn" buttonType="RAISED" maxHeight="-Infinity" <GridPane.margin>
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" <Insets/>
text="PAGE_UP" GridPane.columnIndex="1" GridPane.halignment="CENTER" </GridPane.margin>
GridPane.rowIndex="6" GridPane.valignment="CENTER"/> </Label>
<JFXButton id="DOWNWIND" fx:id="downwindBtn" buttonType="RAISED" maxHeight="-Infinity" <Label text="TACK/GYBE" GridPane.halignment="CENTER"
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" GridPane.rowIndex="5" GridPane.valignment="CENTER">
text="PAGE_DOWN" GridPane.columnIndex="1" GridPane.halignment="CENTER" <GridPane.margin>
GridPane.rowIndex="7" GridPane.valignment="CENTER"/> <Insets/>
<JFXToggleButton fx:id="turningToggle" minHeight="-Infinity" prefHeight="35.0" </GridPane.margin>
text="OFF / ON" GridPane.columnIndex="1" GridPane.halignment="CENTER" </Label>
GridPane.rowIndex="8"/> <Label fx:id="upwindLabel" text="UPWIND"
<Label text="CONTINUOUSLY TURNING" GridPane.halignment="CENTER" GridPane.rowIndex="8" GridPane.halignment="CENTER" GridPane.rowIndex="6"
GridPane.valignment="CENTER"> GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets/> <Insets/>
</GridPane.margin> </GridPane.margin>
</Label> </Label>
<Label fx:id="downwindLabel" text="DOWNWIND"
GridPane.halignment="CENTER" GridPane.rowIndex="7"
GridPane.valignment="CENTER">
<GridPane.margin>
<Insets/>
</GridPane.margin>
</Label>
<JFXButton id="ZOOM IN" fx:id="zoomInbtn" buttonType="RAISED"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0"
minWidth="-Infinity" prefWidth="120.0" text="Z"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="1" GridPane.valignment="CENTER"/>
<JFXButton id="ZOOM OUT" fx:id="zoomOutBtn" buttonType="RAISED"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0"
minWidth="-Infinity" prefWidth="120.0" text="X"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="2" GridPane.valignment="CENTER"/>
<JFXButton id="VMG" fx:id="vmgBtn" buttonType="RAISED"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0"
minWidth="-Infinity" prefWidth="120.0" text="SPACE"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="3" GridPane.valignment="CENTER"/>
<JFXButton id="SAILS IN/OUT" fx:id="sailInOutBtn"
buttonType="RAISED" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity"
prefWidth="120.0" text="SHIFT" GridPane.columnIndex="1"
GridPane.halignment="CENTER" GridPane.rowIndex="4"
GridPane.valignment="CENTER"/>
<JFXButton id="TACK/GYBE" fx:id="tackGybeBtn"
buttonType="RAISED" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity"
prefWidth="120.0" text="ENTER" GridPane.columnIndex="1"
GridPane.halignment="CENTER" GridPane.rowIndex="5"
GridPane.valignment="CENTER"/>
<JFXButton id="UPWIND" fx:id="upwindBtn" buttonType="RAISED"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0"
minWidth="-Infinity" prefWidth="120.0" text="PAGE_UP"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="6" GridPane.valignment="CENTER"/>
<JFXButton id="DOWNWIND" fx:id="downwindBtn"
buttonType="RAISED" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity"
prefWidth="120.0" text="PAGE_DOWN" GridPane.columnIndex="1"
GridPane.halignment="CENTER" GridPane.rowIndex="7"
GridPane.valignment="CENTER"/>
<JFXToggleButton fx:id="turningToggle" minHeight="-Infinity"
prefHeight="35.0" text="OFF / ON" GridPane.columnIndex="1"
GridPane.halignment="CENTER" GridPane.rowIndex="8"/>
<Label text="CONTINUOUSLY TURNING" GridPane.halignment="CENTER"
GridPane.rowIndex="8" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets/>
</GridPane.margin>
</Label>
<Label styleClass="sectionLabel" text="BOAT ACTIONS"
GridPane.columnSpan="2" GridPane.halignment="CENTER"
GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="50.0"/>
</GridPane.margin>
</Label>
<Label styleClass="sectionLabel" text="CAMERA SETTINGS"
GridPane.columnSpan="2" GridPane.halignment="CENTER"
GridPane.rowIndex="9" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="50.0"/>
</GridPane.margin>
</Label>
</children>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity"
minWidth="-Infinity" prefWidth="250.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity"
minWidth="-Infinity" prefWidth="150.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="50.0" minHeight="50.0"
prefHeight="50.0" vgrow="SOMETIMES"/>
</rowConstraints>
</GridPane>
</children>
</StackPane>
</content>
</ScrollPane>
<Label fx:id="keyBindingDialogHeader" text="CUSTOM KEYBINDING" GridPane.columnSpan="2"
GridPane.halignment="CENTER" GridPane.valignment="CENTER"/>
<Label fx:id="closeLabel" text="✖" GridPane.halignment="RIGHT"
GridPane.valignment="TOP"/>
<JFXButton fx:id="resetBtn" buttonType="RAISED" maxHeight="-Infinity" <JFXButton fx:id="resetBtn" buttonType="RAISED" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="45.0" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="45.0"
prefWidth="150.0" text="RESET" GridPane.columnSpan="2" GridPane.halignment="CENTER" prefWidth="150.0" text="RESET" GridPane.columnSpan="2" GridPane.halignment="CENTER"
GridPane.rowIndex="9" GridPane.valignment="CENTER"/> GridPane.rowIndex="2" GridPane.valignment="CENTER"/>
<Label fx:id="closeLabel" text="✖" translateY="-5.0" GridPane.columnIndex="1"
GridPane.halignment="RIGHT" GridPane.valignment="TOP"/>
</children> </children>
</GridPane> </GridPane>
</children> </children>
<stylesheets>
<URL value="@../../css/dialogs/KeyBindingDialog.css" />
<URL value="@../../css/Master.css" />
</stylesheets>
</JFXDialogLayout> </JFXDialogLayout>
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXDialogLayout?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<JFXDialogLayout maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefWidth="550.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.dialogs.PopupDialogController">
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="-Infinity" minHeight="30.0" prefHeight="30.0"
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="80.0"
prefHeight="80.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<JFXButton fx:id="optionButton" buttonType="RAISED" prefHeight="55.0"
prefWidth="150.0" GridPane.halignment="RIGHT" GridPane.rowIndex="2"
GridPane.valignment="CENTER">
<GridPane.margin>
<Insets/>
</GridPane.margin>
</JFXButton>
<Label fx:id="contentLabel" text="Popup content goes here ..."
GridPane.rowIndex="1"/>
<Label fx:id="headerLabel" text="Popup header"/>
<Label fx:id="closeLabel" text="✖" translateY="-10.0" GridPane.halignment="RIGHT"/>
</children>
</GridPane>
</children>
<stylesheets>
<URL value="@../../css/dialogs/Popup.css"/>
<URL value="@../../css/Master.css"/>
</stylesheets>
</JFXDialogLayout>
@@ -110,7 +110,7 @@ public class ChatCommandsTest {
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
Assert.assertEquals(5.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(5.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); mst.terminate();
try { try {
Thread.sleep(200); Thread.sleep(200);
@@ -150,7 +150,7 @@ public class ChatCommandsTest {
ie.printStackTrace(); ie.printStackTrace();
} }
mst.terminate(); mst.terminate();
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
@@ -194,7 +194,7 @@ public class ChatCommandsTest {
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); mst.terminate();
host.setSocketToClose(); host.setSocketToClose();
client.setSocketToClose(); client.setSocketToClose();
@@ -0,0 +1,50 @@
package seng302.utilities;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import seng302.model.GeoPoint;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark;
import seng302.model.token.Token;
/**
* Created by wmu16 on 27/09/17.
*/
public class RandomSpawnTest {
private RandomSpawn randomSpawn;
Mark mark1 = new Mark("mark1", 0, 57.670333, 11.827833, 0);
Mark mark2 = new Mark("mark2", 1, 57.671829, 11.842049, 1);
CompoundMark compoundMark1 = new CompoundMark(0, "mark1",
new ArrayList<>(Arrays.asList(mark1)));
CompoundMark compoundMark2 = new CompoundMark(0, "mark1",
new ArrayList<>(Arrays.asList(mark2)));
List<CompoundMark> markOrder = new ArrayList<>(Arrays.asList(compoundMark1, compoundMark2));
@Before
public void setup() {
randomSpawn = new RandomSpawn(markOrder);
}
@Test
public void testGetRandomTokenLocation() {
GeoPoint testMidPoint = GeoUtility
.getDirtyMidPoint(compoundMark1.getMidPoint(), compoundMark2.getMidPoint());
Double maxDistance = GeoUtility.getDistance(testMidPoint, compoundMark2.getMidPoint());
for (int i = 0; i < 1000; i++) {
Token token = randomSpawn.getRandomToken();
Double distanceFromCentreRadius = GeoUtility.getDistance(testMidPoint, token);
assertTrue("Out of bounds token", distanceFromCentreRadius <= maxDistance);
}
}
}