mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch 'develop' into 1276_Next_Mark_Indicator
# Conflicts: # src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelFactory.java
This commit is contained in:
@@ -63,7 +63,7 @@ public class App extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
ViewManager.getInstance().initialStartView(primaryStage);
|
||||
ViewManager.getInstance().initialiseSplashScreen(primaryStage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javafx.scene.paint.Color;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@@ -23,8 +24,6 @@ import seng302.gameServer.messages.MarkRoundingMessage;
|
||||
import seng302.gameServer.messages.MarkType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RoundingBoatStatus;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.gameServer.messages.YachtEventType;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.Player;
|
||||
@@ -36,6 +35,7 @@ import seng302.model.mark.MarkOrder;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.RandomSpawn;
|
||||
import seng302.utilities.XMLParser;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
@@ -54,24 +54,38 @@ public class GameState implements Runnable {
|
||||
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 PREPATORY_TIME = 5 * -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;
|
||||
private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
|
||||
//Rounding Constants
|
||||
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;
|
||||
public static final Double YACHT_COLLISION_DISTANCE = 25.0;
|
||||
private static final Double BOUNCE_DISTANCE_MARK = 20.0;
|
||||
public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
|
||||
private static final Double COLLISION_VELOCITY_PENALTY = 0.3;
|
||||
|
||||
//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;
|
||||
public static Double windDirection;
|
||||
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 playerHasLeftFlag;
|
||||
@@ -83,13 +97,12 @@ public class GameState implements Runnable {
|
||||
private static GameStages currentStage;
|
||||
private static MarkOrder markOrder;
|
||||
private static long startTime;
|
||||
private static Set<Mark> marks;
|
||||
private static List<Mark> marks;
|
||||
private static List<Limit> courseLimit;
|
||||
private static Integer maxPlayers = 8;
|
||||
|
||||
|
||||
private static List<Token> allTokens;
|
||||
private static List<Token> tokensInPlay;
|
||||
private static RandomSpawn randomSpawn;
|
||||
|
||||
private static List<NewMessageListener> newMessageListeners;
|
||||
|
||||
@@ -100,26 +113,22 @@ public class GameState implements Runnable {
|
||||
windSpeed = 10000d;
|
||||
yachts = new HashMap<>();
|
||||
tokensInPlay = new ArrayList<>();
|
||||
|
||||
players = new ArrayList<>();
|
||||
GameState.hostIpAddress = hostIpAddress;
|
||||
customizationFlag = false;
|
||||
playerHasLeftFlag = false;
|
||||
speedMultiplier = 1.0;
|
||||
serverSpeedMultiplier = 1.0;
|
||||
currentStage = GameStages.LOBBYING;
|
||||
isRaceStarted = false;
|
||||
//set this when game stage changes to prerace
|
||||
previousUpdateTime = System.currentTimeMillis();
|
||||
markOrder = new MarkOrder(); //This could be instantiated at some point with a select map?
|
||||
newMessageListeners = new ArrayList<>();
|
||||
allTokens = makeTokens();
|
||||
marks = new MarkOrder().getAllMarks();
|
||||
randomSpawn = new RandomSpawn(markOrder.getOrderedUniqueCompoundMarks());
|
||||
|
||||
resetStartTime();
|
||||
|
||||
new Thread(this, "GameState").start(); //Run the auto updates on the game state
|
||||
|
||||
marks = new MarkOrder().getAllMarks();
|
||||
setCourseLimit("/server_config/race.xml");
|
||||
new Thread(this, "GameState").start(); //Run the auto updates on the game state
|
||||
}
|
||||
|
||||
private void setCourseLimit(String url) {
|
||||
@@ -137,29 +146,10 @@ public class GameState implements Runnable {
|
||||
courseLimit = XMLParser.parseRace(document).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 String getHostIpAddress() {
|
||||
return hostIpAddress;
|
||||
}
|
||||
|
||||
public static Set<Mark> getMarks() {
|
||||
return Collections.unmodifiableSet(marks);
|
||||
}
|
||||
|
||||
public static List<Player> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
@@ -260,16 +250,75 @@ public class GameState implements Runnable {
|
||||
} catch (InterruptedException e) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
ServerYacht playerYacht = yachts.get(sourceId);
|
||||
switch (actionType) {
|
||||
@@ -304,10 +353,12 @@ public class GameState implements Runnable {
|
||||
* Randomly select a subset of tokensInPlay from a pre defined superset
|
||||
* Broadasts a new race status message to show this update
|
||||
*/
|
||||
public static void spawnNewToken() {
|
||||
Random random = new Random();
|
||||
private void spawnNewToken() {
|
||||
tokensInPlay.clear();
|
||||
tokensInPlay.add(allTokens.get(random.nextInt(allTokens.size())));
|
||||
Token token = randomSpawn.getRandomToken();
|
||||
// token.assignType(TokenType.RANDOM);
|
||||
logger.debug("Spawned token of type " + token.getTokenType());
|
||||
tokensInPlay.add(token);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,14 +375,12 @@ public class GameState implements Runnable {
|
||||
|
||||
Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0;
|
||||
previousUpdateTime = System.currentTimeMillis();
|
||||
if (System.currentTimeMillis() > startTime) {
|
||||
GameState.setCurrentStage(GameStages.RACING);
|
||||
}
|
||||
|
||||
for (ServerYacht yacht : yachts.values()) {
|
||||
updateVelocity(yacht);
|
||||
checkPowerUpTimeout(yacht);
|
||||
yacht.runAutoPilot();
|
||||
yacht.updateLocation(timeInterval);
|
||||
preformTokenUpdates(yacht); //This update must be done before collision. Sorta hacky
|
||||
checkCollision(yacht);
|
||||
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
||||
checkForLegProgression(yacht);
|
||||
@@ -345,17 +394,136 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
private void checkPowerUpTimeout(ServerYacht yacht) {
|
||||
if (yacht.getPowerUp() != null) {
|
||||
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > POWERUP_TIMEOUT_MS) {
|
||||
yacht.powerDown();
|
||||
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired");
|
||||
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
|
||||
/**
|
||||
* All token functionality entry points is taken care of here. So can be disabled and enabled
|
||||
* easily
|
||||
*
|
||||
* @param yacht The yacht to perform token checks on
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -377,13 +545,15 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all tokensInPlay to see if a yacht has picked one up
|
||||
* @return Token which was collided with
|
||||
* @param serverYacht The yacht to check for collision with a token
|
||||
* Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up
|
||||
* in the appropriate way
|
||||
* @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) {
|
||||
Double distance = GeoUtility.getDistance(token, serverYacht.getLocation());
|
||||
Double distance = GeoUtility.getDistance(token, yacht.getLocation());
|
||||
if (distance < YACHT_COLLISION_DISTANCE) {
|
||||
return token;
|
||||
}
|
||||
@@ -406,7 +576,7 @@ public class GameState implements Runnable {
|
||||
*/
|
||||
public static void checkCollision(ServerYacht serverYacht) {
|
||||
//Yacht Collision
|
||||
ServerYacht collidedYacht = checkYachtCollision(serverYacht);
|
||||
ServerYacht collidedYacht = checkYachtCollision(serverYacht, false);
|
||||
Mark collidedMark = checkMarkCollision(serverYacht);
|
||||
|
||||
if (collidedYacht != null) {
|
||||
@@ -423,9 +593,7 @@ public class GameState implements Runnable {
|
||||
collidedYacht.setCurrentVelocity(
|
||||
collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
|
||||
}
|
||||
|
||||
//Mark Collision
|
||||
@@ -437,9 +605,7 @@ public class GameState implements Runnable {
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
|
||||
}
|
||||
|
||||
//Boundary Collision
|
||||
@@ -452,22 +618,7 @@ public class GameState implements Runnable {
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
}
|
||||
|
||||
//Token Collision
|
||||
Token collidedToken = checkTokenPickUp(serverYacht);
|
||||
if (collidedToken != null) {
|
||||
sendServerMessage(serverYacht.getSourceId(), serverYacht.getBoatName() + " has picked speed-up token");
|
||||
tokensInPlay.remove(collidedToken);
|
||||
serverYacht.powerUp(collidedToken.getTokenType());
|
||||
logger.debug("Yacht: " + serverYacht.getShortName() + " got powerup " + collidedToken
|
||||
.getTokenType());
|
||||
notifyMessageListeners(MessageFactory.getRaceXML());
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.TOKEN));
|
||||
notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,29 +626,30 @@ public class GameState implements Runnable {
|
||||
private void updateVelocity(ServerYacht yacht) {
|
||||
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
|
||||
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
|
||||
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier * yacht.getMaxSpeedMultiplier();
|
||||
if (yacht.getPowerUp() != null) {
|
||||
if (yacht.getPowerUp().equals(TokenType.BOOST)) {
|
||||
// TODO: 11/09/17 wmu16 CHANGE THIS TO MAGIC NUMBER
|
||||
maxBoatSpeed *= 2;
|
||||
}
|
||||
}
|
||||
Double maxBoatSpeed =
|
||||
GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht
|
||||
.getPowerUpSpeedMultiplier() * yacht.getBoatTypeSpeedMultiplier();
|
||||
|
||||
Double currentVelocity = yacht.getCurrentVelocity();
|
||||
// TODO: 15/08/17 remove magic numbers from these equations.
|
||||
if (yacht.getSailIn()) {
|
||||
if (currentVelocity < maxBoatSpeed - 500) {
|
||||
yacht.changeVelocity((maxBoatSpeed / 100) * yacht.getAccelerationMultiplier());
|
||||
yacht.changeVelocity(
|
||||
(maxBoatSpeed / 100) * yacht.getBoatTypeAccelerationMultiplier());
|
||||
} else if (currentVelocity > maxBoatSpeed + 500) {
|
||||
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier());
|
||||
yacht.changeVelocity(
|
||||
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
|
||||
} else {
|
||||
yacht.setCurrentVelocity((maxBoatSpeed) * yacht.getAccelerationMultiplier());
|
||||
yacht
|
||||
.setCurrentVelocity((maxBoatSpeed) * yacht.getBoatTypeAccelerationMultiplier());
|
||||
}
|
||||
} else {
|
||||
if (currentVelocity > 3000) {
|
||||
yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier());
|
||||
yacht.changeVelocity(
|
||||
(-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier());
|
||||
} else if (currentVelocity > 100) {
|
||||
yacht.changeVelocity((-currentVelocity / 50) * yacht.getAccelerationMultiplier());
|
||||
yacht.changeVelocity(
|
||||
(-currentVelocity / 50) * yacht.getBoatTypeAccelerationMultiplier());
|
||||
} else if (currentVelocity <= 100) {
|
||||
yacht.setCurrentVelocity(0d);
|
||||
}
|
||||
@@ -560,7 +712,10 @@ public class GameState implements Runnable {
|
||||
|
||||
if (hasProgressed) {
|
||||
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();
|
||||
sendMarkRoundingMessage(yacht);
|
||||
@@ -596,7 +751,9 @@ public class GameState implements Runnable {
|
||||
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
|
||||
yacht.setClosestCurrentMark(mark1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -700,7 +857,10 @@ public class GameState implements Runnable {
|
||||
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
|
||||
yacht.setClosestCurrentMark(mark1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -723,6 +883,7 @@ public class GameState implements Runnable {
|
||||
String name = new String(customizeData);
|
||||
playerYacht.setBoatName(name);
|
||||
} 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 green = customizeData[1] & 0xFF;
|
||||
int blue = customizeData[2] & 0xFF;
|
||||
@@ -735,7 +896,7 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
private static Mark checkMarkCollision(ServerYacht yacht) {
|
||||
Set<Mark> marksInRace = GameState.getMarks();
|
||||
Set<Mark> marksInRace = new HashSet<>(marks);
|
||||
for (Mark mark : marksInRace) {
|
||||
if (GeoUtility.getDistance(yacht.getLocation(), mark)
|
||||
<= MARK_COLLISION_DISTANCE) {
|
||||
@@ -764,15 +925,22 @@ public class GameState implements Runnable {
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
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()) {
|
||||
if (otherYacht != yacht) {
|
||||
Double distance = GeoUtility
|
||||
.getDistance(otherYacht.getLocation(), yacht.getLocation());
|
||||
if (distance < YACHT_COLLISION_DISTANCE) {
|
||||
;
|
||||
if (distance < collisionDistance) {
|
||||
return otherYacht;
|
||||
}
|
||||
}
|
||||
@@ -808,13 +976,6 @@ public class GameState implements Runnable {
|
||||
roundingMark.getSourceID()));
|
||||
}
|
||||
|
||||
|
||||
public static void sendServerMessage(Integer messageType, String message) {
|
||||
notifyMessageListeners(new ChatterMessage(
|
||||
messageType, "SERVER: " + message
|
||||
));
|
||||
}
|
||||
|
||||
public static void processChatter(ChatterMessage chatterMessage, boolean isHost) {
|
||||
String chatterText = chatterMessage.getMessage();
|
||||
String[] words = chatterText.split("\\s+");
|
||||
@@ -822,17 +983,19 @@ public class GameState implements Runnable {
|
||||
switch (words[2].trim()) {
|
||||
case "/speed":
|
||||
try {
|
||||
setSpeedMultiplier(Double.valueOf(words[3]));
|
||||
sendServerMessage(chatterMessage.getMessage_type(),
|
||||
"Speed modifier set to x" + words[3]);
|
||||
serverSpeedMultiplier = Double.valueOf(words[3]);
|
||||
String logMessage = "Speed modifier set to x" + words[3];
|
||||
notifyMessageListeners(MessageFactory
|
||||
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
|
||||
} catch (Exception e) {
|
||||
Logger logger = LoggerFactory.getLogger(GameState.class);
|
||||
logger.error("cannot parse >speed value");
|
||||
}
|
||||
return;
|
||||
case "/finish":
|
||||
sendServerMessage(chatterMessage.getMessage_type(),
|
||||
"Game will now finish");
|
||||
String logMessage = "Game will now finish";
|
||||
notifyMessageListeners(MessageFactory
|
||||
.makeChatterMessage(chatterMessage.getMessageType(), logMessage));
|
||||
endRace();
|
||||
return;
|
||||
}
|
||||
@@ -889,11 +1052,7 @@ public class GameState implements Runnable {
|
||||
currentStage = GameStages.FINISHED;
|
||||
}
|
||||
|
||||
public static void setSpeedMultiplier (double multiplier) {
|
||||
speedMultiplier = multiplier;
|
||||
}
|
||||
|
||||
public static double getSpeedMultiplier () {
|
||||
return speedMultiplier;
|
||||
public static double getServerSpeedMultiplier() {
|
||||
return serverSpeedMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
private static final int PORT = 4942;
|
||||
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 Thread thread;
|
||||
@@ -101,8 +98,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
GameState.addMessageEventListener(this::broadcastMessage);
|
||||
terminated = false;
|
||||
thread = new Thread(this, "MainServer");
|
||||
startUpdatingWind();
|
||||
startSpawningTokens();
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@@ -187,63 +182,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
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -11,11 +12,14 @@ 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.ServerYacht;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
|
||||
/**
|
||||
@@ -128,4 +132,73 @@ public class MessageFactory {
|
||||
XMLMessageSubType.BOAT,
|
||||
xmlGenerator.getBoatsAsXml().length());
|
||||
}
|
||||
|
||||
public static YachtEventCodeMessage makeCollisionMessage(ServerYacht serverYacht) {
|
||||
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) {
|
||||
YachtEventType yachtEventType = null;
|
||||
switch (token.getTokenType()) {
|
||||
case BOOST:
|
||||
yachtEventType = YachtEventType.TOKEN_VELOCITY;
|
||||
break;
|
||||
case HANDLING:
|
||||
yachtEventType = YachtEventType.TOKEN_HANDLING;
|
||||
break;
|
||||
case WIND_WALKER:
|
||||
yachtEventType = YachtEventType.TOKEN_WIND_WALKER;
|
||||
break;
|
||||
case BUMPER:
|
||||
yachtEventType = YachtEventType.TOKEN_BUMPER;
|
||||
break;
|
||||
case RANDOM:
|
||||
yachtEventType = YachtEventType.TOKEN_RANDOM;
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ public class ServerToClientThread implements Runnable {
|
||||
private List<ConnectionListener> connectionListeners = new ArrayList<>();
|
||||
private DisconnectListener disconnectListener;
|
||||
|
||||
private ServerYacht yacht;
|
||||
private Player player;
|
||||
|
||||
public ServerToClientThread(Socket socket) {
|
||||
@@ -101,33 +100,12 @@ public class ServerToClientThread implements Runnable {
|
||||
}
|
||||
|
||||
private void setUpPlayer(){
|
||||
BufferedReader fn;
|
||||
String fName = "";
|
||||
BufferedReader ln;
|
||||
String lName = "";
|
||||
String shortName = "p" + sourceId;
|
||||
String longName = "player " + sourceId;
|
||||
|
||||
fn = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
ServerToClientThread.class.getResourceAsStream(
|
||||
"/server_config/CSV_Database_of_First_Names.csv"
|
||||
)
|
||||
)
|
||||
);
|
||||
List<String> all = fn.lines().collect(Collectors.toList());
|
||||
fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||
ln = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
ServerToClientThread.class.getResourceAsStream(
|
||||
"/server_config/CSV_Database_of_Last_Names.csv"
|
||||
)
|
||||
)
|
||||
);
|
||||
all = ln.lines().collect(Collectors.toList());
|
||||
lName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||
|
||||
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);
|
||||
GameState.addYacht(sourceId, yacht);
|
||||
@@ -318,10 +296,6 @@ public class ServerToClientThread implements Runnable {
|
||||
return socket;
|
||||
}
|
||||
|
||||
public ServerYacht getYacht() {
|
||||
return yacht;
|
||||
}
|
||||
|
||||
public void addConnectionListener(ConnectionListener listener) {
|
||||
connectionListeners.add(listener);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class ChatterMessage extends Message {
|
||||
return message;
|
||||
}
|
||||
|
||||
public int getMessage_type() {
|
||||
public int getMessageType() {
|
||||
return message_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package seng302.gameServer.messages;
|
||||
|
||||
/**
|
||||
* Created by wmu16 on 11/09/17.
|
||||
* Enum for different event types for the yacht
|
||||
*/
|
||||
public enum YachtEventType {
|
||||
COLLISION(33),
|
||||
TOKEN(34);
|
||||
TOKEN_VELOCITY(34),
|
||||
TOKEN_BUMPER(35),
|
||||
TOKEN_HANDLING(36),
|
||||
TOKEN_WIND_WALKER(37),
|
||||
TOKEN_RANDOM(38),
|
||||
POWER_DOWN(39),
|
||||
BUMPER_CRASH(40);
|
||||
|
||||
|
||||
private int code;
|
||||
|
||||
|
||||
@@ -13,9 +13,13 @@ import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.beans.property.ReadOnlyLongProperty;
|
||||
import javafx.beans.property.ReadOnlyLongWrapper;
|
||||
import javafx.beans.value.ObservableObjectValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.paint.Color;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatObject;
|
||||
|
||||
@@ -37,6 +41,26 @@ public class ClientYacht extends Observable {
|
||||
void notifyRounding(ClientYacht yacht, int legNumber);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ColorChangeListener {
|
||||
|
||||
void notifyColorChange(ClientYacht yacht);
|
||||
}
|
||||
|
||||
//This notifies RaceViewController so it can display icon - wmu16
|
||||
@FunctionalInterface
|
||||
public interface PowerUpListener {
|
||||
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);
|
||||
|
||||
|
||||
@@ -47,6 +71,7 @@ public class ClientYacht extends Observable {
|
||||
private String boatName;
|
||||
private String country;
|
||||
private Integer position;
|
||||
private TokenType powerUp;
|
||||
|
||||
private Long estimateTimeAtFinish;
|
||||
private Boolean sailIn = true;
|
||||
@@ -65,6 +90,10 @@ public class ClientYacht extends Observable {
|
||||
|
||||
private List<YachtLocationListener> locationListeners = new ArrayList<>();
|
||||
private List<MarkRoundingListener> markRoundingListeners = 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 ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
|
||||
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
|
||||
@@ -208,6 +237,32 @@ public class ClientYacht extends Observable {
|
||||
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) {
|
||||
this.powerUp = tokenType;
|
||||
for (PowerUpListener listener : powerUpListeners) {
|
||||
listener.notifyPowerUp(this, tokenType);
|
||||
}
|
||||
}
|
||||
|
||||
public TokenType getPowerUp() {
|
||||
return powerUp;
|
||||
}
|
||||
|
||||
public void toggleSail() {
|
||||
sailIn = !sailIn;
|
||||
}
|
||||
@@ -257,6 +312,9 @@ public class ClientYacht extends Observable {
|
||||
|
||||
public void setColour(Color colour) {
|
||||
this.colour = colour;
|
||||
for (ColorChangeListener listener : colorChangeListeners) {
|
||||
listener.notifyColorChange(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
||||
@@ -282,6 +340,18 @@ public class ClientYacht extends Observable {
|
||||
markRoundingListeners.add(listener);
|
||||
}
|
||||
|
||||
public void addPowerUpListener(PowerUpListener 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) {
|
||||
markRoundingListeners.remove(listener);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Collections;
|
||||
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
|
||||
@@ -17,6 +19,7 @@ public final class PolarTable {
|
||||
private static HashMap<Double, HashMap<Double, Double>> polarTable;
|
||||
private static HashMap<Double, HashMap<Double, Double>> upwindOptimal;
|
||||
private static HashMap<Double, HashMap<Double, Double>> downwindOptimal;
|
||||
private static Double optimalAngle;
|
||||
|
||||
private static int upTwaIndex;
|
||||
private static int dnTwaIndex;
|
||||
@@ -33,11 +36,13 @@ public final class PolarTable {
|
||||
upwindOptimal = new HashMap<>();
|
||||
downwindOptimal = new HashMap<>();
|
||||
|
||||
String line;
|
||||
String line = null;
|
||||
String check;
|
||||
Boolean isHeaderLine = true;
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) {
|
||||
while ((line = br.readLine()) != null) {
|
||||
while ((check = br.readLine()) != null) {
|
||||
line = check;
|
||||
String[] thisLine = line.split(",");
|
||||
|
||||
//Initial line in file
|
||||
@@ -66,7 +71,10 @@ public final class PolarTable {
|
||||
upwindOptimal.put(thisWindSpeed, thisUpWindPolar);
|
||||
downwindOptimal.put(thisWindSpeed, thisDnWindPolar);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
getMaxSpeedAngle(line);
|
||||
|
||||
} catch (IOException e) {
|
||||
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
|
||||
@@ -85,14 +114,18 @@ public final class PolarTable {
|
||||
String thisItem = thisLine[i];
|
||||
if (thisItem.toLowerCase().startsWith("uptwa")) {
|
||||
upTwaIndex = i;
|
||||
}
|
||||
else if (thisItem.toLowerCase().startsWith("dntwa")) {
|
||||
} else if (thisItem.toLowerCase().startsWith("dntwa")) {
|
||||
dnTwaIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Double getOptimalAngle() {
|
||||
return optimalAngle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return The entire polar table
|
||||
*/
|
||||
|
||||
@@ -23,9 +23,9 @@ public class ServerYacht {
|
||||
//Boat info
|
||||
private BoatMeshType boatType;
|
||||
private Double turnStep = 5.0;
|
||||
private Double maxSpeedMultiplier = 1.0;
|
||||
private Double turnStepMultiplier = 1.0;
|
||||
private Double accelerationMultiplier = 1.0;
|
||||
private Double boatTypeSpeedMultiplier = 1.0;
|
||||
private Double boatTypeTurnStepMultiplier = 1.0;
|
||||
private Double boatTypeAccelerationMultiplier = 1.0;
|
||||
private Integer sourceId;
|
||||
private String hullID; //matches HullNum in the XML spec.
|
||||
private String shortName;
|
||||
@@ -55,6 +55,8 @@ public class ServerYacht {
|
||||
//PowerUp
|
||||
private TokenType powerUp;
|
||||
private Long powerUpStartTime;
|
||||
private Double powerUpSpeedMultiplier;
|
||||
private Integer powerUpHandlingMultiplier;
|
||||
|
||||
//turning mode
|
||||
private Boolean continuouslyTurning;
|
||||
@@ -78,11 +80,11 @@ public class ServerYacht {
|
||||
this.legNumber = 0;
|
||||
this.boatColor = Colors.getColor(sourceId - 1);
|
||||
this.powerUp = null;
|
||||
|
||||
this.powerUpSpeedMultiplier = 1d;
|
||||
this.powerUpHandlingMultiplier = 1;
|
||||
this.hasEnteredRoundingZone = false;
|
||||
this.hasPassedLine = false;
|
||||
this.hasPassedThroughGate = false;
|
||||
|
||||
this.continuouslyTurning = false;
|
||||
}
|
||||
|
||||
@@ -110,13 +112,33 @@ public class ServerYacht {
|
||||
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) {
|
||||
powerDown();
|
||||
switch (powerUp) {
|
||||
case BOOST:
|
||||
powerUpSpeedMultiplier = GameState.VELOCITY_BOOST_MULTIPLIER;
|
||||
break;
|
||||
case HANDLING:
|
||||
powerUpHandlingMultiplier = GameState.HANDLING_BOOST_MULTIPLIER;
|
||||
break;
|
||||
}
|
||||
this.powerUp = powerUp;
|
||||
powerUpStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Powers down a yacht, returning its power multipliers back to 1
|
||||
*/
|
||||
public void powerDown() {
|
||||
this.powerUp = null;
|
||||
this.powerUpSpeedMultiplier = 1d;
|
||||
this.powerUpHandlingMultiplier = 1;
|
||||
}
|
||||
|
||||
public Long getPowerUpStartTime() {
|
||||
@@ -133,7 +155,7 @@ public class ServerYacht {
|
||||
* @param amount the amount by which to adjust the boat heading.
|
||||
*/
|
||||
public void adjustHeading(Double amount) {
|
||||
Double newVal = heading + (amount * turnStepMultiplier);
|
||||
Double newVal = heading + amount * powerUpHandlingMultiplier * boatTypeTurnStepMultiplier;
|
||||
lastHeading = heading;
|
||||
heading = (double) Math.floorMod(newVal.longValue(), 360L);
|
||||
}
|
||||
@@ -435,18 +457,18 @@ public class ServerYacht {
|
||||
}
|
||||
|
||||
public void setBoatType(BoatMeshType boatType) {
|
||||
this.accelerationMultiplier = boatType.accelerationMultiplier;
|
||||
this.maxSpeedMultiplier = boatType.maxSpeedMultiplier;
|
||||
this.turnStepMultiplier = boatType.turnStep;
|
||||
this.boatTypeAccelerationMultiplier = boatType.accelerationMultiplier;
|
||||
this.boatTypeSpeedMultiplier = boatType.maxSpeedMultiplier;
|
||||
this.boatTypeTurnStepMultiplier = boatType.turnStep;
|
||||
this.boatType = boatType;
|
||||
}
|
||||
|
||||
public Double getMaxSpeedMultiplier() {
|
||||
return maxSpeedMultiplier;
|
||||
public Double getBoatTypeSpeedMultiplier() {
|
||||
return boatTypeSpeedMultiplier;
|
||||
}
|
||||
|
||||
public Double getAccelerationMultiplier(){
|
||||
return accelerationMultiplier;
|
||||
public Double getBoatTypeAccelerationMultiplier() {
|
||||
return boatTypeAccelerationMultiplier;
|
||||
}
|
||||
|
||||
|
||||
@@ -457,4 +479,20 @@ public class ServerYacht {
|
||||
public void setContinuouslyTurning(Boolean 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ import java.util.*;
|
||||
*/
|
||||
public class MarkOrder {
|
||||
private List<CompoundMark> raceMarkOrder;
|
||||
private List<CompoundMark> orderedUniqueCompoundMarks;
|
||||
private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
|
||||
private Set<Mark> allMarks;
|
||||
private List<Mark> allMarks;
|
||||
|
||||
public MarkOrder(){
|
||||
loadRaceProperties();
|
||||
@@ -44,6 +45,10 @@ public class MarkOrder {
|
||||
return Collections.unmodifiableList(raceMarkOrder);
|
||||
}
|
||||
|
||||
public List<CompoundMark> getOrderedUniqueCompoundMarks() {
|
||||
return orderedUniqueCompoundMarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
@@ -75,8 +80,8 @@ public class MarkOrder {
|
||||
return raceMarkOrder.get(currentSeqID + 1);
|
||||
}
|
||||
|
||||
public Set<Mark> getAllMarks(){
|
||||
return Collections.unmodifiableSet(allMarks);
|
||||
public List<Mark> getAllMarks() {
|
||||
return Collections.unmodifiableList(allMarks);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +94,7 @@ public class MarkOrder {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
Document doc;
|
||||
allMarks = new HashSet<>();
|
||||
allMarks = new ArrayList<>();
|
||||
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
@@ -105,6 +110,7 @@ public class MarkOrder {
|
||||
logger.debug("Loaded RaceXML for mark order");
|
||||
List<Corner> corners = data.getMarkSequence();
|
||||
Map<Integer, CompoundMark> marks = data.getCompoundMarks();
|
||||
orderedUniqueCompoundMarks = new ArrayList<>(marks.values());
|
||||
List<CompoundMark> course = new ArrayList<>();
|
||||
for (Corner corner : corners){
|
||||
CompoundMark compoundMark = marks.get(corner.getCompoundMarkID());
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package seng302.model.token;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import seng302.model.GeoPoint;
|
||||
|
||||
/**
|
||||
@@ -9,13 +13,50 @@ import seng302.model.GeoPoint;
|
||||
public class Token extends GeoPoint {
|
||||
|
||||
private TokenType tokenType;
|
||||
private Random random = new Random();
|
||||
|
||||
//Constructor for creating a specific type client side
|
||||
public Token(TokenType tokenType, double lat, double lng) {
|
||||
super(lat, lng);
|
||||
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() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a random type to the token (including the random type token)
|
||||
*/
|
||||
public void assignRandomType() {
|
||||
tokenType = TokenType.values()[random.nextInt(TokenType.values().length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a random, concrete type to the token (cannot be the random type)
|
||||
*/
|
||||
public void realiseRandom() {
|
||||
List<TokenType> tokenTypeList = new ArrayList<>(Arrays.asList(TokenType.values()));
|
||||
tokenTypeList.remove(TokenType.RANDOM);
|
||||
tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists for testing purposes only
|
||||
*/
|
||||
public void assignType(TokenType tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,27 +5,32 @@ package seng302.model.token;
|
||||
* Created by wmu16 on 28/08/17.
|
||||
*/
|
||||
public enum TokenType {
|
||||
BOOST(0),
|
||||
HANDLING(1);
|
||||
|
||||
BOOST(0, "Boost", 10_000),
|
||||
HANDLING(1, "Handling", 10_000),
|
||||
BUMPER(2, "Bumper", 10_000),
|
||||
WIND_WALKER(3, "Wind Walker", 10_000),
|
||||
RANDOM(4, "Random", 10_000);
|
||||
|
||||
private int value;
|
||||
private String name;
|
||||
private int timeout;
|
||||
|
||||
TokenType(int value) {
|
||||
TokenType(int value, String name, int timeout) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static TokenType getToken(int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return BOOST;
|
||||
case 1:
|
||||
return HANDLING;
|
||||
default:
|
||||
return BOOST;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,6 +101,10 @@ public class Sounds {
|
||||
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
|
||||
musicPlayer.setVolume(0.3);
|
||||
musicPlayer.play();
|
||||
musicPlayer.setMute(musicMuted);
|
||||
if (soundEffect != null) {
|
||||
soundEffect.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ 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.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -17,6 +19,7 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.util.Pair;
|
||||
import seng302.gameServer.GameStages;
|
||||
import seng302.gameServer.GameState;
|
||||
@@ -37,6 +40,7 @@ import seng302.model.stream.parser.RaceStatusData;
|
||||
import seng302.model.stream.parser.YachtEventData;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.utilities.StreamParser;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
@@ -251,12 +255,7 @@ public class GameClient {
|
||||
break;
|
||||
|
||||
case YACHT_EVENT_CODE:
|
||||
YachtEventData yachtEventData = StreamParser.extractYachtEventCode(packet);
|
||||
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
|
||||
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN.getCode()) {
|
||||
showPickUp();
|
||||
}
|
||||
processYachtEvent(StreamParser.extractYachtEventCode(packet));
|
||||
break;
|
||||
|
||||
case CHATTER_TEXT:
|
||||
@@ -276,12 +275,13 @@ public class GameClient {
|
||||
|
||||
ClientYacht player = allBoatsMap.get(socketThread.getClientId());
|
||||
raceView.loadRace(allBoatsMap, courseData, raceState, player);
|
||||
|
||||
raceView.showView();
|
||||
raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> {
|
||||
if (isPressed) {
|
||||
formatAndSendChatMessage(raceView.readChatInput());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,21 +415,66 @@ public class GameClient {
|
||||
return courseData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appropriately displays the event client side given the YachtEventCode (collision / token..)
|
||||
*
|
||||
* @param yachtEventData The YachtEvent data packet
|
||||
*/
|
||||
private void processYachtEvent(YachtEventData yachtEventData) {
|
||||
ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue());
|
||||
|
||||
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
|
||||
showCollisionAlert(thisYacht);
|
||||
} 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;
|
||||
if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) {
|
||||
tokenType = TokenType.BOOST;
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_BUMPER.getCode()) {
|
||||
tokenType = TokenType.BUMPER;
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_HANDLING.getCode()) {
|
||||
tokenType = TokenType.HANDLING;
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_RANDOM.getCode()) {
|
||||
tokenType = TokenType.RANDOM;
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN_WIND_WALKER.getCode()) {
|
||||
tokenType = TokenType.WIND_WALKER;
|
||||
}
|
||||
|
||||
Sounds.playTokenPickupSound();
|
||||
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.
|
||||
*/
|
||||
private void showCollisionAlert(YachtEventData yachtEventData) {
|
||||
private void showCollisionAlert(ClientYacht yacht) {
|
||||
Sounds.playCrashSound();
|
||||
raceState.storeCollision(
|
||||
allBoatsMap.get(
|
||||
yachtEventData.getSubjectId().intValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user
|
||||
private void showPickUp() {
|
||||
Sounds.playTokenPickupSound();
|
||||
raceState.storeCollision(yacht);
|
||||
}
|
||||
|
||||
private void formatAndSendChatMessage(String rawChat) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.cameras.ChaseCamera;
|
||||
@@ -126,8 +127,6 @@ public class GameView3D {
|
||||
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
||||
@@ -421,28 +420,30 @@ public class GameView3D {
|
||||
public void cameraMovement(KeyEvent event) {
|
||||
GameKeyBind keyBinds = GameKeyBind.getInstance();
|
||||
KeyAction keyPressed = keyBinds.getKeyAction(event.getCode());
|
||||
switch (keyPressed) {
|
||||
case ZOOM_IN:
|
||||
((RaceCamera) view.getCamera()).zoomIn();
|
||||
break;
|
||||
case ZOOM_OUT:
|
||||
((RaceCamera) view.getCamera()).zoomOut();
|
||||
break;
|
||||
case FORWARD:
|
||||
((RaceCamera) view.getCamera()).panUp();
|
||||
break;
|
||||
case BACKWARD:
|
||||
((RaceCamera) view.getCamera()).panDown();
|
||||
break;
|
||||
case LEFT:
|
||||
((RaceCamera) view.getCamera()).panLeft();
|
||||
break;
|
||||
case RIGHT:
|
||||
((RaceCamera) view.getCamera()).panRight();
|
||||
break;
|
||||
case VIEW:
|
||||
toggleCamera();
|
||||
break;
|
||||
if (keyPressed != null) {
|
||||
switch (keyPressed) {
|
||||
case ZOOM_IN:
|
||||
((RaceCamera) view.getCamera()).zoomIn();
|
||||
break;
|
||||
case ZOOM_OUT:
|
||||
((RaceCamera) view.getCamera()).zoomOut();
|
||||
break;
|
||||
case FORWARD:
|
||||
((RaceCamera) view.getCamera()).panUp();
|
||||
break;
|
||||
case BACKWARD:
|
||||
((RaceCamera) view.getCamera()).panDown();
|
||||
break;
|
||||
case LEFT:
|
||||
((RaceCamera) view.getCamera()).panLeft();
|
||||
break;
|
||||
case RIGHT:
|
||||
((RaceCamera) view.getCamera()).panRight();
|
||||
break;
|
||||
case VIEW:
|
||||
toggleCamera();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,11 +487,8 @@ public class GameView3D {
|
||||
wakesGroup.getChildren().add(newBoat.getWake());
|
||||
wakes.add(newBoat.getWake());
|
||||
boatObjectGroup.getChildren().add(newBoat);
|
||||
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
|
||||
BoatObject bo = boatObjects.get(boat);
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
||||
});
|
||||
clientYacht.addLocationListener(this::updateBoatLocation);
|
||||
clientYacht.addColorChangeListener(this::updateBoatColor);
|
||||
|
||||
if (clientYacht.getSourceId().equals(
|
||||
ViewManager.getInstance().getGameClient().getServerThread().getClientId())) {
|
||||
@@ -535,6 +533,23 @@ public class GameView3D {
|
||||
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
|
||||
* border already exists. Assumes the border is larger than the course.
|
||||
@@ -610,7 +625,27 @@ public class GameView3D {
|
||||
mapTokens = new ArrayList<>();
|
||||
for (Token token : newTokens) {
|
||||
Point2D location = findScaledXY(token.getLat(), token.getLng());
|
||||
Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets();
|
||||
|
||||
ModelType modelType = null;
|
||||
switch (token.getTokenType()) {
|
||||
case BOOST:
|
||||
modelType = ModelType.VELOCITY_PICKUP;
|
||||
break;
|
||||
case HANDLING:
|
||||
modelType = ModelType.HANDLING_PICKUP;
|
||||
break;
|
||||
case BUMPER:
|
||||
modelType = ModelType.BUMPER_PICKUP;
|
||||
break;
|
||||
case RANDOM:
|
||||
modelType = ModelType.RANDOM_PICKUP;
|
||||
break;
|
||||
case WIND_WALKER:
|
||||
modelType = ModelType.WIND_WALKER_PICKUP;
|
||||
break;
|
||||
}
|
||||
|
||||
Node tokenObject = ModelFactory.importModel(modelType).getAssets();
|
||||
tokenObject.setLayoutX(location.getX());
|
||||
tokenObject.setLayoutY(location.getY());
|
||||
mapTokens.add(tokenObject);
|
||||
|
||||
@@ -20,6 +20,8 @@ import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.SubScene;
|
||||
import javafx.scene.chart.LineChart;
|
||||
@@ -27,12 +29,8 @@ import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.chart.XYChart.Data;
|
||||
import javafx.scene.chart.XYChart.Series;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
@@ -46,11 +44,15 @@ import javafx.scene.shape.Polyline;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import javax.swing.ImageIcon;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.model.ClientYacht.PowerUpListener;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameView3D;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
|
||||
@@ -60,6 +62,8 @@ import seng302.visualiser.controllers.dialogs.FinishDialogController;
|
||||
import seng302.visualiser.fxObjects.ChatHistory;
|
||||
import seng302.visualiser.fxObjects.assets_2D.WindArrow;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatObject;
|
||||
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
|
||||
import seng302.visualiser.fxObjects.assets_3D.ModelType;
|
||||
|
||||
/**
|
||||
* Controller class that manages the display of a race
|
||||
@@ -67,7 +71,13 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject;
|
||||
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
||||
|
||||
private final int CHAT_LIMIT = 128;
|
||||
private static final Double ICON_BLINK_TIMEOUT_RATIO = 0.6;
|
||||
private static final Integer ICON_BLINK_PERIOD = 500;
|
||||
|
||||
@FXML
|
||||
private AnchorPane loadingScreenPane;
|
||||
@FXML
|
||||
private ImageView loadingScreen;
|
||||
@FXML
|
||||
private Pane basePane;
|
||||
@FXML
|
||||
@@ -110,6 +120,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
private Label windSpeedLabel;
|
||||
@FXML
|
||||
private Label positionLabel, boatSpeedLabel, boatHeadingLabel;
|
||||
@FXML
|
||||
private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon;
|
||||
|
||||
//Race Data
|
||||
private Map<Integer, ClientYacht> participants;
|
||||
@@ -130,7 +142,29 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
private JFXDialog finishScreenDialog;
|
||||
private FinishDialogController finishDialogController;
|
||||
|
||||
//Icon stuff
|
||||
private Timer blinkingTimer = new Timer();
|
||||
private ImageView iconToDisplay;
|
||||
|
||||
public void initialize() {
|
||||
contentStackPane.setVisible(false);
|
||||
Image loadingImage = new Image("PP.png");
|
||||
loadingScreen.setImage(loadingImage);
|
||||
//Centers the Image within the image view
|
||||
double w = 0;
|
||||
double h = 0;
|
||||
double ratioX = loadingScreen.getFitWidth() / loadingImage.getWidth();
|
||||
double ratioY = loadingScreen.getFitHeight() / loadingImage.getHeight();
|
||||
double reduceRatio = 0;
|
||||
if(ratioX >= ratioY) {
|
||||
reduceRatio = ratioY;
|
||||
} else {
|
||||
reduceRatio = ratioX;
|
||||
}
|
||||
w = loadingImage.getWidth() * reduceRatio;
|
||||
h = loadingImage.getHeight() * reduceRatio;
|
||||
loadingScreen.setX((loadingScreen.getFitWidth() - w) / 2);
|
||||
loadingScreen.setY((loadingScreen.getFitHeight() - h) / 2);
|
||||
Sounds.stopMusic();
|
||||
Sounds.playRaceMusic();
|
||||
|
||||
@@ -202,6 +236,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
createFinishDialog(finishedBoats);
|
||||
}
|
||||
|
||||
public void showView(){
|
||||
loadingScreenPane.setVisible(false);
|
||||
contentStackPane.setVisible(true);
|
||||
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
contentStackPane.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create finishScreenDialog and set up finishDialogController.
|
||||
*/
|
||||
@@ -222,6 +268,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void loadRace (
|
||||
Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState,
|
||||
ClientYacht player) {
|
||||
@@ -241,6 +288,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
}
|
||||
});
|
||||
|
||||
player.addPowerUpListener(this::displayPowerUpIcon);
|
||||
player.addPowerDownListener(this::removeIcon);
|
||||
|
||||
updateOrder(raceState.getPlayerPositions());
|
||||
gameView = new GameView3D();
|
||||
// gameView.setFrameRateFXText(fpsDisplay);
|
||||
@@ -279,6 +329,68 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the relevant icon, starts blinking it when it is close to turning off and then
|
||||
* switches it off after the tokens time out
|
||||
*
|
||||
* @param yacht The yacht only for which we are displaying the icon
|
||||
* @param tokenType The type of token, indicating what icon needs to be displayed
|
||||
*/
|
||||
private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) {
|
||||
if (yacht == player) {
|
||||
if (iconToDisplay != null) {
|
||||
iconToDisplay.setVisible(false);
|
||||
}
|
||||
|
||||
switch (tokenType) {
|
||||
case BOOST:
|
||||
iconToDisplay = velocityIcon;
|
||||
break;
|
||||
case HANDLING:
|
||||
iconToDisplay = handlingIcon;
|
||||
break;
|
||||
case WIND_WALKER:
|
||||
iconToDisplay = windWalkerIcon;
|
||||
break;
|
||||
case BUMPER:
|
||||
iconToDisplay = bumperIcon;
|
||||
break;
|
||||
case RANDOM:
|
||||
iconToDisplay = badRandomIcon;
|
||||
break;
|
||||
default:
|
||||
iconToDisplay = velocityIcon;
|
||||
}
|
||||
|
||||
//Turn icon on
|
||||
iconToDisplay.setVisible(true);
|
||||
|
||||
//Start blinking icon towards end
|
||||
if (blinkingTimer != null) {
|
||||
blinkingTimer.cancel();
|
||||
}
|
||||
blinkingTimer = new Timer("Blinking Timer");
|
||||
blinkingTimer.schedule(new TimerTask() {
|
||||
Boolean isVisible = true;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
isVisible = !isVisible;
|
||||
iconToDisplay.setVisible(isVisible);
|
||||
}
|
||||
}, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeIcon(ClientYacht yacht) {
|
||||
if (yacht == player) {
|
||||
blinkingTimer.cancel();
|
||||
iconToDisplay.setVisible(false);
|
||||
iconToDisplay = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The important annotations have been changed, update this view
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.jfoenix.controls.JFXTextField;
|
||||
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;
|
||||
@@ -27,6 +28,7 @@ import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.ServerListener;
|
||||
import seng302.visualiser.ServerListenerDelegate;
|
||||
import seng302.visualiser.controllers.cells.ServerCell;
|
||||
import seng302.visualiser.controllers.dialogs.ServerCreationController;
|
||||
import seng302.visualiser.validators.HostNameFieldValidator;
|
||||
import seng302.visualiser.validators.NumberRangeValidator;
|
||||
import seng302.visualiser.validators.ValidationTools;
|
||||
@@ -55,6 +57,14 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
|
||||
private Label noServersFound;
|
||||
private Logger logger = LoggerFactory.getLogger(ServerListController.class);
|
||||
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.
|
||||
@Override
|
||||
@@ -115,6 +125,8 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
serverListHostButton.setOnAction(action -> {
|
||||
showServerCreationDialog();
|
||||
});
|
||||
|
||||
addServerCreationDialogListener(this::closeServerCreationDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,9 +137,11 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
FXMLLoader dialogContent = new FXMLLoader(getClass().getResource(
|
||||
"/views/dialogs/ServerCreationDialog.fxml"));
|
||||
try {
|
||||
JFXDialog dialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
|
||||
serverCreationDialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
|
||||
DialogTransition.CENTER);
|
||||
dialog.show();
|
||||
ServerCreationController serverCreationController = dialogContent.getController();
|
||||
serverCreationController.setListener(serverCreationDialogListeners);
|
||||
serverCreationDialog.show();
|
||||
Sounds.playButtonClick();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not create Server Creation Dialog.");
|
||||
@@ -135,6 +149,10 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
});
|
||||
}
|
||||
|
||||
private void closeServerCreationDialog() {
|
||||
serverCreationDialog.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the connection and attempts to connect to a given hostname and port number.
|
||||
*/
|
||||
@@ -203,4 +221,14 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
public void serverDetected(ServerDescription serverDescription, List<ServerDescription> servers) {
|
||||
Platform.runLater(() -> refreshServers(servers));
|
||||
}
|
||||
|
||||
private void addServerCreationDialogListener(
|
||||
ServerCreationDialogListener serverCreationDialogListener) {
|
||||
serverCreationDialogListeners.add(serverCreationDialogListener);
|
||||
}
|
||||
|
||||
private void removeServerCreationDialogListener(
|
||||
ServerCreationDialogListener serverCreationDialogListener) {
|
||||
serverCreationDialogListeners.remove(serverCreationDialogListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package seng302.visualiser.controllers;
|
||||
|
||||
import com.jfoenix.controls.JFXDecorator;
|
||||
import com.jfoenix.controls.JFXSnackbar;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.SceneAntialiasing;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
import seng302.gameServer.ServerAdvertiser;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Created by Kusal on 26-Sep-17.
|
||||
*/
|
||||
public class SplashScreenController implements Initializable{
|
||||
|
||||
@FXML
|
||||
private StackPane rootPane;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
new SplashScreen().start();
|
||||
}
|
||||
|
||||
|
||||
class SplashScreen extends Thread {
|
||||
public void run(){
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Stage stage = new Stage();
|
||||
ViewManager.getInstance().initialStartView(stage);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
rootPane.getScene().getWindow().hide();
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.ServerAdvertiser;
|
||||
@@ -56,10 +57,18 @@ public class ViewManager {
|
||||
if (instance == null) {
|
||||
instance = new ViewManager();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void initialiseSplashScreen(Stage stage) throws IOException {
|
||||
this.stage = stage;
|
||||
Parent root = FXMLLoader.load(getClass().getResource("/views/SplashScreen.fxml"));
|
||||
Scene scene = new Scene(root);
|
||||
stage.setScene(scene);
|
||||
stage.initStyle(StageStyle.UNDECORATED);
|
||||
stage.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the start view in the given stage.
|
||||
*/
|
||||
@@ -67,7 +76,6 @@ public class ViewManager {
|
||||
this.stage = stage;
|
||||
Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml"));
|
||||
stage.setTitle("Party Parrots At Sea");
|
||||
|
||||
JFXDecorator decorator = new JFXDecorator(stage, root, false, true, true);
|
||||
decorator.setCustomMaximize(true);
|
||||
decorator.applyCss();
|
||||
@@ -102,8 +110,6 @@ public class ViewManager {
|
||||
gameClient.stopGame();
|
||||
System.exit(0);
|
||||
});
|
||||
|
||||
jfxSnackbar = new JFXSnackbar(decorator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,6 +194,7 @@ public class ViewManager {
|
||||
}
|
||||
});
|
||||
|
||||
jfxSnackbar = new JFXSnackbar(decorator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +228,7 @@ public class ViewManager {
|
||||
.getController();
|
||||
keyBindingDialogController.setGameClient(this.gameClient);
|
||||
keyBindingDialog.show();
|
||||
decorator.requestFocus();
|
||||
Sounds.playButtonClick();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -71,8 +71,7 @@ public class KeyBindingDialogController implements Initializable {
|
||||
buttons = new ArrayList<>();
|
||||
Collections.addAll(buttons,
|
||||
zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn,
|
||||
viewButton,
|
||||
rightButton, leftButton, forwardButton, backwardButton);
|
||||
viewButton, rightButton, leftButton, forwardButton, backwardButton);
|
||||
bindButtonWithAction();
|
||||
loadKeyBind();
|
||||
|
||||
@@ -88,12 +87,10 @@ public class KeyBindingDialogController implements Initializable {
|
||||
resetBtn.setOnMouseClicked(event -> {
|
||||
gameKeyBind.setToDefault();
|
||||
loadKeyBind();
|
||||
showSnackBar("All keys reset!", false);
|
||||
});
|
||||
|
||||
closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog());
|
||||
|
||||
keyBindingDialogHeader.setFocusTraversable(true);
|
||||
keyBindingDialogHeader.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +158,7 @@ public class KeyBindingDialogController implements Initializable {
|
||||
+ "-fx-background-color: -fx-pp-front-color; "
|
||||
+ "-fx-text-fill: -fx-pp-theme-color; "
|
||||
+ "-fx-font-size: 13;");
|
||||
keyBindingDialogHeader.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,14 +5,15 @@ import com.jfoenix.controls.JFXSlider;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.validation.RequiredFieldValidator;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import seng302.gameServer.ServerDescription;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.controllers.ServerListController.ServerCreationDialogListener;
|
||||
import seng302.visualiser.controllers.ViewManager;
|
||||
import seng302.visualiser.validators.FieldLengthValidator;
|
||||
import seng302.visualiser.validators.ValidationTools;
|
||||
@@ -28,8 +29,12 @@ public class ServerCreationController implements Initializable {
|
||||
private Label maxPlayersLabel;
|
||||
@FXML
|
||||
private JFXButton submitBtn;
|
||||
@FXML
|
||||
private Label closeLabel;
|
||||
//---------FXML END---------//
|
||||
|
||||
private List<ServerCreationDialogListener> serverCreationDialogListeners;
|
||||
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
updateMaxPlayerLabel();
|
||||
maxPlayersSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
@@ -49,6 +54,7 @@ public class ServerCreationController implements Initializable {
|
||||
validateServerSettings();
|
||||
});
|
||||
|
||||
closeLabel.setOnMouseClicked(event -> notifyListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,4 +93,14 @@ public class ServerCreationController implements Initializable {
|
||||
Sounds.playHoverSound();
|
||||
}
|
||||
|
||||
public void setListener(List<ServerCreationDialogListener> serverCreationDialogListeners) {
|
||||
this.serverCreationDialogListeners = serverCreationDialogListeners;
|
||||
}
|
||||
|
||||
public void notifyListeners() {
|
||||
for (ServerCreationDialogListener serverCreationDialogListener : serverCreationDialogListeners) {
|
||||
serverCreationDialogListener.notifyClosure();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ public enum BoatMeshType {
|
||||
CATAMARAN("catamaran_hull.stl", "catamaran_mast.stl", 0.997, "catamaran_sail.stl",
|
||||
0.997, null, false, 1.0, 1.4, 2.0),
|
||||
PIRATE_SHIP("pirateship_hull.stl", "pirateship_mast.stl", -0.5415, "pirateship_mainsail.stl",
|
||||
-0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2);
|
||||
-0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2),
|
||||
DUCKY("ducky_hull.stl", "ducky_mast.stl", -2.18539, "ducky_sail.stl", -2.18539, "ducky_eyes.stl", false, 1.2, 1.1, 1.4);
|
||||
|
||||
final String hullFile, mastFile, sailFile, jibFile;
|
||||
final double mastOffset, sailOffset;
|
||||
@@ -19,7 +20,7 @@ public enum BoatMeshType {
|
||||
public final double accelerationMultiplier;
|
||||
public final double turnStep;
|
||||
final boolean fixedSail;
|
||||
final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP};
|
||||
final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP, DUCKY};
|
||||
|
||||
BoatMeshType(String hullFile, String mastFile, double mastOffset, String sailFile,
|
||||
double sailOffset, String jibFile, boolean fixedSail, double maxSpeedMultiplier, double accelerationMultiplier, double turnStep) {
|
||||
|
||||
@@ -157,7 +157,11 @@ public class ModelFactory {
|
||||
case NEXT_MARK_INDICATOR:
|
||||
return makeNextMarkIndicator(assets);
|
||||
case VELOCITY_PICKUP:
|
||||
return makeCoinPickup(assets);
|
||||
case BUMPER_PICKUP:
|
||||
case RANDOM_PICKUP:
|
||||
case HANDLING_PICKUP:
|
||||
case WIND_WALKER_PICKUP:
|
||||
return makeTokenPickup(assets);
|
||||
case FINISH_MARKER:
|
||||
case PLAIN_MARKER:
|
||||
case START_MARKER:
|
||||
@@ -191,24 +195,22 @@ public class ModelFactory {
|
||||
return new Model(new Group(assets), null);
|
||||
}
|
||||
|
||||
private static Model makeCoinPickup(Group assets) {
|
||||
assets.setRotationAxis(new Point3D(1,0,0));
|
||||
assets.setRotate(90);
|
||||
assets.setTranslateX(0.2);
|
||||
assets.setTranslateY(1);
|
||||
private static Model makeTokenPickup(Group assets) {
|
||||
Rotate animationRotate = new Rotate(0, new Point3D(0, 0, 1));
|
||||
assets.getTransforms().addAll(
|
||||
new Translate(0,-1,0),
|
||||
new Rotate(0 ,new Point3D(1,1,1))
|
||||
animationRotate,
|
||||
new Translate(0, 0, -1)
|
||||
);
|
||||
|
||||
return new Model(new Group(assets), new AnimationTimer() {
|
||||
|
||||
private double rotation = 0;
|
||||
private Group group = assets;
|
||||
private Rotate rotate = animationRotate;
|
||||
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
rotation += 1;
|
||||
((Rotate) group.getTransforms().get(1)).setAngle(rotation);
|
||||
rotate.setAngle(rotation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ package seng302.visualiser.fxObjects.assets_3D;
|
||||
public enum ModelType {
|
||||
|
||||
VELOCITY_PICKUP("velocity_pickup.dae"),
|
||||
HANDLING_PICKUP("turning_pickup.dae"),
|
||||
WIND_WALKER_PICKUP("wind_walker_pickup.dae"),
|
||||
BUMPER_PICKUP("bumper_pickup.dae"),
|
||||
RANDOM_PICKUP("random_pickup.dae"),
|
||||
FINISH_MARKER ("finish_marker.dae"),
|
||||
START_MARKER ("start_marker.dae"),
|
||||
PLAIN_MARKER ("plain_marker.dae"),
|
||||
|
||||
Reference in New Issue
Block a user