mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -6,16 +6,21 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import org.w3c.dom.Document;
|
||||||
import seng302.gameServer.server.messages.BoatStatus;
|
import org.xml.sax.InputSource;
|
||||||
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.gameServer.server.messages.MarkType;
|
import seng302.gameServer.messages.BoatStatus;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.MarkRoundingMessage;
|
||||||
import seng302.gameServer.server.messages.RoundingBoatStatus;
|
import seng302.gameServer.messages.MarkType;
|
||||||
import seng302.gameServer.server.messages.YachtEventCodeMessage;
|
import seng302.gameServer.messages.Message;
|
||||||
|
import seng302.gameServer.messages.RoundingBoatStatus;
|
||||||
|
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
|
import seng302.model.Limit;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.PolarTable;
|
import seng302.model.PolarTable;
|
||||||
import seng302.model.ServerYacht;
|
import seng302.model.ServerYacht;
|
||||||
@@ -23,6 +28,7 @@ import seng302.model.mark.CompoundMark;
|
|||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.MarkOrder;
|
import seng302.model.mark.MarkOrder;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
import seng302.utilities.XMLParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Static class to hold information about the current state of the game (model)
|
* A Static class to hold information about the current state of the game (model)
|
||||||
@@ -30,9 +36,9 @@ import seng302.utilities.GeoUtility;
|
|||||||
* Created by wmu16 on 10/07/17.
|
* Created by wmu16 on 10/07/17.
|
||||||
*/
|
*/
|
||||||
public class GameState implements Runnable {
|
public class GameState implements Runnable {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface NewMessageListener {
|
interface NewMessageListener {
|
||||||
|
|
||||||
void notify(Message message);
|
void notify(Message message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +65,7 @@ public class GameState implements Runnable {
|
|||||||
private static MarkOrder markOrder;
|
private static MarkOrder markOrder;
|
||||||
private static long startTime;
|
private static long startTime;
|
||||||
private static Set<Mark> marks;
|
private static Set<Mark> marks;
|
||||||
|
private static List<Limit> courseLimit;
|
||||||
|
|
||||||
private static List<NewMessageListener> markListeners;
|
private static List<NewMessageListener> markListeners;
|
||||||
|
|
||||||
@@ -81,7 +88,7 @@ public class GameState implements Runnable {
|
|||||||
yachts = new HashMap<>();
|
yachts = new HashMap<>();
|
||||||
players = new ArrayList<>();
|
players = new ArrayList<>();
|
||||||
GameState.hostIpAddress = hostIpAddress;
|
GameState.hostIpAddress = hostIpAddress;
|
||||||
;
|
|
||||||
currentStage = GameStages.LOBBYING;
|
currentStage = GameStages.LOBBYING;
|
||||||
isRaceStarted = false;
|
isRaceStarted = false;
|
||||||
//set this when game stage changes to prerace
|
//set this when game stage changes to prerace
|
||||||
@@ -89,16 +96,34 @@ public class GameState implements Runnable {
|
|||||||
markOrder = new MarkOrder(); //This could be instantiated at some point with a select map?
|
markOrder = new MarkOrder(); //This could be instantiated at some point with a select map?
|
||||||
markListeners = new ArrayList<>();
|
markListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
resetStartTime();
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
marks = new MarkOrder().getAllMarks();
|
marks = new MarkOrder().getAllMarks();
|
||||||
|
setCourseLimit("/server_config/race.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCourseLimit(String url) {
|
||||||
|
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
documentBuilderFactory.setNamespaceAware(true);
|
||||||
|
DocumentBuilder documentBuilder;
|
||||||
|
Document document = null;
|
||||||
|
try {
|
||||||
|
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||||
|
document = documentBuilder.parse(new InputSource(getClass().getResourceAsStream(url)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// sorry, we have to catch general one, otherwise we have to catch five different exceptions.
|
||||||
|
logger.trace("Failed to load course limit for boundary collision detection.", e);
|
||||||
|
}
|
||||||
|
courseLimit = XMLParser.parseRace(document).getCourseLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getHostIpAddress() {
|
public static String getHostIpAddress() {
|
||||||
return hostIpAddress;
|
return hostIpAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<Mark> getMarks(){
|
public static Set<Mark> getMarks() {
|
||||||
return Collections.unmodifiableSet(marks);
|
return Collections.unmodifiableSet(marks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,10 +160,6 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setCurrentStage(GameStages currentStage) {
|
public static void setCurrentStage(GameStages currentStage) {
|
||||||
if (currentStage == GameStages.RACING) {
|
|
||||||
startTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
GameState.currentStage = currentStage;
|
GameState.currentStage = currentStage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,14 +167,26 @@ public class GameState implements Runnable {
|
|||||||
return markOrder;
|
return markOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getStartTime(){
|
public static long getStartTime() {
|
||||||
return startTime;
|
return startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void resetStartTime(){
|
||||||
|
startTime = System.currentTimeMillis() + MainServerThread.TIME_TILL_START;
|
||||||
|
}
|
||||||
|
|
||||||
public static Double getWindDirection() {
|
public static Double getWindDirection() {
|
||||||
return windDirection;
|
return windDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setWindDirection(Double newWindDirection) {
|
||||||
|
windDirection = newWindDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWindSpeed(Double newWindSpeed) {
|
||||||
|
windSpeed = newWindSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
public static Double getWindSpeedMMS() {
|
public static Double getWindSpeedMMS() {
|
||||||
return windSpeed;
|
return windSpeed;
|
||||||
}
|
}
|
||||||
@@ -190,7 +223,7 @@ 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) {
|
if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +257,6 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called periodically in this GameState thread to update the GameState values
|
* Called periodically in this GameState thread to update the GameState values
|
||||||
*/
|
*/
|
||||||
@@ -233,15 +265,20 @@ 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);
|
||||||
yacht.runAutoPilot();
|
yacht.runAutoPilot();
|
||||||
yacht.updateLocation(timeInterval);
|
yacht.updateLocation(timeInterval);
|
||||||
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
||||||
checkForCollision(yacht);
|
checkCollision(yacht);
|
||||||
checkForLegProgression(yacht);
|
checkForLegProgression(yacht);
|
||||||
raceFinished = false;
|
raceFinished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (raceFinished) {
|
if (raceFinished) {
|
||||||
@@ -249,9 +286,28 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the yacht has crossed the course limit
|
||||||
|
*
|
||||||
|
* @param yacht the yacht to be tested
|
||||||
|
* @return a boolean value of if there is a boundary collision
|
||||||
|
*/
|
||||||
|
private static Boolean checkBoundaryCollision(ServerYacht yacht) {
|
||||||
|
for (int i = 0; i < courseLimit.size() - 1; i++) {
|
||||||
|
if (GeoUtility.checkCrossedLine(courseLimit.get(i), courseLimit.get(i + 1),
|
||||||
|
yacht.getLastLocation(), yacht.getLocation()) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0),
|
||||||
|
yacht.getLastLocation(), yacht.getLocation()) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void checkForCollision(ServerYacht serverYacht) {
|
public static void checkCollision(ServerYacht serverYacht) {
|
||||||
ServerYacht collidedYacht = checkCollision(serverYacht);
|
ServerYacht collidedYacht = checkYachtCollision(serverYacht);
|
||||||
if (collidedYacht != null) {
|
if (collidedYacht != null) {
|
||||||
GeoPoint originalLocation = serverYacht.getLocation();
|
GeoPoint originalLocation = serverYacht.getLocation();
|
||||||
serverYacht.setLocation(
|
serverYacht.setLocation(
|
||||||
@@ -270,7 +326,7 @@ public class GameState implements Runnable {
|
|||||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Mark collidedMark = markCollidedWith(serverYacht);
|
Mark collidedMark = checkMarkCollision(serverYacht);
|
||||||
if (collidedMark != null) {
|
if (collidedMark != null) {
|
||||||
serverYacht.setLocation(
|
serverYacht.setLocation(
|
||||||
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
||||||
@@ -282,6 +338,20 @@ public class GameState implements Runnable {
|
|||||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
if (checkBoundaryCollision(serverYacht)) {
|
||||||
|
serverYacht.setLocation(
|
||||||
|
calculateBounceBack(serverYacht, serverYacht.getLocation(),
|
||||||
|
BOUNCE_DISTANCE_YACHT)
|
||||||
|
);
|
||||||
|
serverYacht.setCurrentVelocity(
|
||||||
|
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||||
|
);
|
||||||
|
notifyMessageListeners(
|
||||||
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +366,7 @@ public class GameState implements Runnable {
|
|||||||
if (velocity < maxBoatSpeed - 500) {
|
if (velocity < maxBoatSpeed - 500) {
|
||||||
yacht.changeVelocity(maxBoatSpeed / 100);
|
yacht.changeVelocity(maxBoatSpeed / 100);
|
||||||
} else if (velocity > maxBoatSpeed + 500) {
|
} else if (velocity > maxBoatSpeed + 500) {
|
||||||
yacht.changeVelocity(-maxBoatSpeed / 100);
|
yacht.changeVelocity(-velocity / 200);
|
||||||
} else {
|
} else {
|
||||||
yacht.setCurrentVelocity(maxBoatSpeed);
|
yacht.setCurrentVelocity(maxBoatSpeed);
|
||||||
}
|
}
|
||||||
@@ -305,7 +375,7 @@ public class GameState implements Runnable {
|
|||||||
yacht.changeVelocity(-velocity / 200);
|
yacht.changeVelocity(-velocity / 200);
|
||||||
} else if (velocity > 100) {
|
} else if (velocity > 100) {
|
||||||
yacht.changeVelocity(-velocity / 50);
|
yacht.changeVelocity(-velocity / 50);
|
||||||
} else if (velocity <= 100){
|
} else if (velocity <= 100) {
|
||||||
yacht.setCurrentVelocity(0d);
|
yacht.setCurrentVelocity(0d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,6 +417,7 @@ public class GameState implements Runnable {
|
|||||||
/**
|
/**
|
||||||
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
||||||
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
||||||
|
*
|
||||||
* @param yacht the current yacht to check for progression
|
* @param yacht the current yacht to check for progression
|
||||||
*/
|
*/
|
||||||
private void checkForLegProgression(ServerYacht yacht) {
|
private void checkForLegProgression(ServerYacht yacht) {
|
||||||
@@ -366,6 +437,7 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasProgressed) {
|
if (hasProgressed) {
|
||||||
|
yacht.incrementLegNumber();
|
||||||
sendMarkRoundingMessage(yacht);
|
sendMarkRoundingMessage(yacht);
|
||||||
logMarkRounding(yacht);
|
logMarkRounding(yacht);
|
||||||
yacht.setHasPassedLine(false);
|
yacht.setHasPassedLine(false);
|
||||||
@@ -510,7 +582,7 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Mark markCollidedWith(ServerYacht yacht) {
|
private static Mark checkMarkCollision(ServerYacht yacht) {
|
||||||
Set<Mark> marksInRace = GameState.getMarks();
|
Set<Mark> marksInRace = GameState.getMarks();
|
||||||
for (Mark mark : marksInRace) {
|
for (Mark mark : marksInRace) {
|
||||||
if (GeoUtility.getDistance(yacht.getLocation(), mark)
|
if (GeoUtility.getDistance(yacht.getLocation(), mark)
|
||||||
@@ -526,12 +598,14 @@ public class GameState implements Runnable {
|
|||||||
*
|
*
|
||||||
* @return The boats new position
|
* @return The boats new position
|
||||||
*/
|
*/
|
||||||
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, Double bounceDistance) {
|
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith,
|
||||||
Double heading = GeoUtility.getBearing(yacht.getLocation(), collidedWith);
|
Double bounceDistance) {
|
||||||
|
Double heading = GeoUtility.getBearing(yacht.getLastLocation(), collidedWith);
|
||||||
// Invert heading
|
// Invert heading
|
||||||
heading -= 180;
|
heading -= 180;
|
||||||
Integer newHeading = Math.floorMod(heading.intValue(), 360);
|
Integer newHeading = Math.floorMod(heading.intValue(), 360);
|
||||||
return GeoUtility.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
|
return GeoUtility
|
||||||
|
.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -540,11 +614,12 @@ public class GameState implements Runnable {
|
|||||||
*
|
*
|
||||||
* @return yacht to compare to all other yachts.
|
* @return yacht to compare to all other yachts.
|
||||||
*/
|
*/
|
||||||
private static ServerYacht checkCollision(ServerYacht yacht) {
|
private static ServerYacht checkYachtCollision(ServerYacht yacht) {
|
||||||
|
|
||||||
for (ServerYacht otherYacht : GameState.getYachts().values()) {
|
for (ServerYacht otherYacht : GameState.getYachts().values()) {
|
||||||
if (otherYacht != yacht) {
|
if (otherYacht != yacht) {
|
||||||
Double distance = GeoUtility.getDistance(otherYacht.getLocation(), yacht.getLocation());
|
Double distance = GeoUtility
|
||||||
|
.getDistance(otherYacht.getLocation(), yacht.getLocation());
|
||||||
if (distance < YACHT_COLLISION_DISTANCE) {
|
if (distance < YACHT_COLLISION_DISTANCE) {
|
||||||
return otherYacht;
|
return otherYacht;
|
||||||
}
|
}
|
||||||
@@ -563,7 +638,7 @@ public class GameState implements Runnable {
|
|||||||
// TODO: 13/8/17 figure out the rounding side, rounded mark source ID and boat status.
|
// TODO: 13/8/17 figure out the rounding side, rounded mark source ID and boat status.
|
||||||
Message markRoundingMessage = new MarkRoundingMessage(0, 0,
|
Message markRoundingMessage = new MarkRoundingMessage(0, 0,
|
||||||
sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType,
|
sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType,
|
||||||
roundingMark.getSourceID());
|
currentMarkSeqID + 1);
|
||||||
|
|
||||||
notifyMessageListeners(markRoundingMessage);
|
notifyMessageListeners(markRoundingMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ import java.util.Stack;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.gameServer.server.messages.Heartbeat;
|
import seng302.gameServer.messages.Heartbeat;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Heartbeat messages to connected player at a specified interval
|
* Send Heartbeat messages to connected player at a specified interval
|
||||||
* Will call .clientDisconnected on the delegate when a heartbeat message
|
* Will call .clientDisconnected on the delegate when a heartbeat message
|
||||||
* cannot be sent to a player
|
* cannot be sent to a player
|
||||||
*/
|
*/
|
||||||
public class HeartbeatThread extends Thread{
|
public class HeartbeatThread implements Runnable {
|
||||||
private final int HEARTBEAT_PERIOD = 200;
|
private final int HEARTBEAT_PERIOD = 200;
|
||||||
private ClientConnectionDelegate delegate;
|
private ClientConnectionDelegate delegate;
|
||||||
private Integer seqNum;
|
private Integer seqNum;
|
||||||
@@ -23,6 +23,9 @@ public class HeartbeatThread extends Thread{
|
|||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
seqNum = 0;
|
seqNum = 0;
|
||||||
disconnectedPlayers = new Stack<>();
|
disconnectedPlayers = new Stack<>();
|
||||||
|
|
||||||
|
Thread thread = new Thread(this, "HeartBeat");
|
||||||
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
package seng302.gameServer;
|
package seng302.gameServer;
|
||||||
|
|
||||||
import gherkin.lexer.Fi;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import seng302.gameServer.server.messages.BoatSubMessage;
|
import seng302.gameServer.messages.BoatSubMessage;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.gameServer.server.messages.RaceStatus;
|
import seng302.gameServer.messages.RaceStartNotificationType;
|
||||||
import seng302.gameServer.server.messages.RaceStatusMessage;
|
import seng302.gameServer.messages.RaceStartStatusMessage;
|
||||||
import seng302.gameServer.server.messages.RaceType;
|
import seng302.gameServer.messages.RaceStatus;
|
||||||
|
import seng302.gameServer.messages.RaceStatusMessage;
|
||||||
|
import seng302.gameServer.messages.RaceType;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.PolarTable;
|
import seng302.model.PolarTable;
|
||||||
@@ -28,8 +30,17 @@ import seng302.visualiser.GameClient;
|
|||||||
public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
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 = 10;
|
private static final Integer CLIENT_UPDATES_PER_SECOND = 60;
|
||||||
private static final int LOG_LEVEL = 1;
|
private static final int LOG_LEVEL = 1;
|
||||||
|
private static final int WARNING_TIME = 10 * -1000;
|
||||||
|
private static final int PREPATORY_TIME = 5 * -1000;
|
||||||
|
public static final int TIME_TILL_START = 10 * 1000;
|
||||||
|
|
||||||
|
private static final int MAX_WIND_SPEED = 12000;
|
||||||
|
private static final int MIN_WIND_SPEED = 8000;
|
||||||
|
|
||||||
|
public static int windSpeed = 1000;
|
||||||
|
|
||||||
private boolean terminated;
|
private boolean terminated;
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
@@ -50,19 +61,15 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
GameState.addMarkPassListener(this::broadcastMessage);
|
GameState.addMarkPassListener(this::broadcastMessage);
|
||||||
terminated = false;
|
terminated = false;
|
||||||
thread = new Thread(this, "MainServer");
|
thread = new Thread(this, "MainServer");
|
||||||
|
startUpdatingWind();
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
ServerListenThread serverListenThread;
|
|
||||||
HeartbeatThread heartbeatThread;
|
|
||||||
|
|
||||||
serverListenThread = new ServerListenThread(serverSocket, this);
|
new HeartbeatThread(this);
|
||||||
heartbeatThread = new HeartbeatThread(this);
|
new ServerListenThread(serverSocket, this);
|
||||||
|
|
||||||
heartbeatThread.start();
|
|
||||||
serverListenThread.start();
|
|
||||||
|
|
||||||
//You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
|
//You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
@@ -108,6 +115,45 @@ 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(100) + 500;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
direction -= random.nextInt(4);
|
||||||
|
windSpeed -= random.nextInt(100) + 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
direction = Math.floorMod(direction, 360);
|
||||||
|
|
||||||
|
if (windSpeed > MAX_WIND_SPEED){
|
||||||
|
windSpeed -= random.nextInt(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windSpeed <= MIN_WIND_SPEED){
|
||||||
|
windSpeed += random.nextInt(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameState.setWindSpeed(Double.valueOf(windSpeed));
|
||||||
|
GameState.setWindDirection(direction.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startUpdatingWind(){
|
||||||
|
Timer timer = new Timer();
|
||||||
|
timer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateWind();
|
||||||
|
}
|
||||||
|
}, 0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void serverLog(String message, int logLevel) {
|
static void serverLog(String message, int logLevel) {
|
||||||
if (logLevel <= LOG_LEVEL) {
|
if (logLevel <= LOG_LEVEL) {
|
||||||
@@ -166,10 +212,21 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
broadcastMessage(makeRaceStatusMessage());
|
broadcastMessage(makeRaceStatusMessage());
|
||||||
|
if (GameState.getCurrentStage() == GameStages.PRE_RACE || GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||||
|
broadcastMessage(makeRaceStartMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 0, 500);
|
}, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private RaceStartStatusMessage makeRaceStartMessage() {
|
||||||
|
Long raceStartTime = GameState.getStartTime();
|
||||||
|
|
||||||
|
return new RaceStartStatusMessage(1, raceStartTime ,
|
||||||
|
1, RaceStartNotificationType.SET_RACE_START_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
private RaceStatusMessage makeRaceStatusMessage() {
|
private RaceStatusMessage makeRaceStatusMessage() {
|
||||||
// variables taken from GameServerThread
|
// variables taken from GameServerThread
|
||||||
|
|
||||||
@@ -178,16 +235,29 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
|||||||
|
|
||||||
for (Player player : GameState.getPlayers()) {
|
for (Player player : GameState.getPlayers()) {
|
||||||
ServerYacht y = player.getYacht();
|
ServerYacht y = player.getYacht();
|
||||||
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(), 0,
|
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(),
|
||||||
|
y.getLegNumber(),
|
||||||
0, 0, 1234L,
|
0, 0, 1234L,
|
||||||
1234L);
|
1234L);
|
||||||
boatSubMessages.add(m);
|
boatSubMessages.add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameState.getCurrentStage() == GameStages.RACING) {
|
long timeTillStart = System.currentTimeMillis() - GameState.getStartTime();
|
||||||
raceStatus = RaceStatus.STARTED;
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||||
|
raceStatus = RaceStatus.PRESTART;
|
||||||
|
} else if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||||
|
raceStatus = RaceStatus.PRESTART;
|
||||||
|
|
||||||
|
if (timeTillStart > WARNING_TIME) {
|
||||||
|
raceStatus = RaceStatus.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeTillStart > PREPATORY_TIME) {
|
||||||
|
raceStatus = RaceStatus.PREPARATORY;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
raceStatus = RaceStatus.WARNING;
|
raceStatus = RaceStatus.STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(),
|
return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(),
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ import java.net.Socket;
|
|||||||
* A class for a thread to listen to connections
|
* A class for a thread to listen to connections
|
||||||
* Created by wmu16 on 11/07/17.
|
* Created by wmu16 on 11/07/17.
|
||||||
*/
|
*/
|
||||||
public class ServerListenThread extends Thread{
|
public class ServerListenThread implements Runnable {
|
||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
private ClientConnectionDelegate delegate;
|
private ClientConnectionDelegate delegate;
|
||||||
|
|
||||||
public ServerListenThread(ServerSocket serverSocket, ClientConnectionDelegate delegate){
|
public ServerListenThread(ServerSocket serverSocket, ClientConnectionDelegate delegate){
|
||||||
this.serverSocket = serverSocket;
|
this.serverSocket = serverSocket;
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
|
||||||
|
Thread thread = new Thread(this, "ServerListen");
|
||||||
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package seng302.gameServer;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import seng302.gameServer.server.messages.ClientType;
|
import seng302.gameServer.messages.ClientType;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
|
|
||||||
|
|
||||||
public class ServerPacketParser {
|
public class ServerPacketParser {
|
||||||
|
|||||||
@@ -19,25 +19,25 @@ import java.util.zip.CRC32;
|
|||||||
import java.util.zip.Checksum;
|
import java.util.zip.Checksum;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.server.messages.YachtEventCodeMessage;
|
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.stream.packets.PacketType;
|
import seng302.model.stream.packets.PacketType;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.model.stream.xml.generator.Race;
|
import seng302.model.stream.xml.generator.Race;
|
||||||
import seng302.model.stream.xml.generator.Regatta;
|
import seng302.model.stream.xml.generator.Regatta;
|
||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.gameServer.server.messages.BoatLocationMessage;
|
import seng302.gameServer.messages.BoatLocationMessage;
|
||||||
import seng302.gameServer.server.messages.BoatSubMessage;
|
import seng302.gameServer.messages.BoatSubMessage;
|
||||||
import seng302.gameServer.server.messages.ClientType;
|
import seng302.gameServer.messages.ClientType;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.gameServer.server.messages.RaceStatus;
|
import seng302.gameServer.messages.RaceStatus;
|
||||||
import seng302.gameServer.server.messages.RaceStatusMessage;
|
import seng302.gameServer.messages.RaceStatusMessage;
|
||||||
import seng302.gameServer.server.messages.RaceType;
|
import seng302.gameServer.messages.RaceType;
|
||||||
import seng302.gameServer.server.messages.RegistrationResponseMessage;
|
import seng302.gameServer.messages.RegistrationResponseMessage;
|
||||||
import seng302.gameServer.server.messages.RegistrationResponseStatus;
|
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||||
import seng302.gameServer.server.messages.XMLMessage;
|
import seng302.gameServer.messages.XMLMessage;
|
||||||
import seng302.gameServer.server.messages.XMLMessageSubType;
|
import seng302.gameServer.messages.XMLMessageSubType;
|
||||||
import seng302.model.ServerYacht;
|
import seng302.model.ServerYacht;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,8 +209,6 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: 24/07/17 zyt10 - fix a logic here when a client disconnected
|
|
||||||
// serverLog("ERROR OCCURRED, CLOSING SERVER CONNECTION: " + socket.getRemoteSocketAddress().toString(), 1);
|
|
||||||
closeSocket();
|
closeSocket();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -226,7 +224,7 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@TODO calculate lat/lng values
|
//@TODO calculate lat/lng values
|
||||||
xml.setRegatta(new Regatta("RaceVision Test Game", 57.6679590, 11.8503233));
|
xml.setRegatta(new Regatta("Party Parrot Test Server", "Bermuda Test Course", 57.6679590, 11.8503233));
|
||||||
xml.setRace(race);
|
xml.setRace(race);
|
||||||
|
|
||||||
XMLMessage xmlMessage;
|
XMLMessage xmlMessage;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by kre39 on 12/07/17.
|
* Created by kre39 on 12/07/17.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public class BoatLocationMessage extends Message {
|
public class BoatLocationMessage extends Message {
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current status of a boat
|
* The current status of a boat
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by kre39 on 20/07/17.
|
* Created by kre39 on 20/07/17.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public enum ClientType {
|
public enum ClientType {
|
||||||
SPECTATOR(0x00),
|
SPECTATOR(0x00),
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public enum DeviceType {
|
public enum DeviceType {
|
||||||
UNKNOWN(0),
|
UNKNOWN(0),
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public class Heartbeat extends Message {
|
public class Heartbeat extends Message {
|
||||||
private final int MESSAGE_SIZE = 4;
|
private final int MESSAGE_SIZE = 4;
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import seng302.gameServer.GameState;
|
|
||||||
|
|
||||||
public class MarkRoundingMessage extends Message{
|
public class MarkRoundingMessage extends Message{
|
||||||
private final long MESSAGE_VERSION_NUMBER = 1;
|
private final long MESSAGE_VERSION_NUMBER = 1;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of marks boats can round
|
* Types of marks boats can round
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum containing the types of messages
|
* Enum containing the types of messages
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The types of race start status messages
|
* The types of race start status messages
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public class RaceStartStatusMessage extends Message {
|
public class RaceStartStatusMessage extends Message {
|
||||||
private final int MESSAGE_SIZE = 20;
|
private final int MESSAGE_SIZE = 20;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current status of the race
|
* The current status of the race
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
@@ -39,7 +39,7 @@ public class RaceStatusMessage extends Message{
|
|||||||
this.raceId = raceId;
|
this.raceId = raceId;
|
||||||
this.raceStatus = raceStatus;
|
this.raceStatus = raceStatus;
|
||||||
this.expectedStartTime = expectedStartTime;
|
this.expectedStartTime = expectedStartTime;
|
||||||
this.raceWindDirection = raceWindDirection * windDirFactor;
|
this.raceWindDirection = raceWindDirection * windDirFactor+100.0;
|
||||||
this.windSpeed = windSpeed;
|
this.windSpeed = windSpeed;
|
||||||
this.numBoatsInRace = numBoatsInRace;
|
this.numBoatsInRace = numBoatsInRace;
|
||||||
this.raceType = raceType;
|
this.raceType = raceType;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum containing the types of races
|
* Enum containing the types of races
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
|
|
||||||
public class RegistrationRequestMessage extends Message {
|
public class RegistrationRequestMessage extends Message {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public class RegistrationResponseMessage extends Message{
|
public class RegistrationResponseMessage extends Message{
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public enum RegistrationResponseStatus {
|
public enum RegistrationResponseStatus {
|
||||||
SUCCESS_SPECTATING(0x00),
|
SUCCESS_SPECTATING(0x00),
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The status of a boat rounding a mark
|
* The status of a boat rounding a mark
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The side the boat rounded the mark
|
* The side the boat rounded the mark
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
public class XMLMessage extends Message{
|
public class XMLMessage extends Message{
|
||||||
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
|
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum containing the types of XML messages
|
* Enum containing the types of XML messages
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package seng302.gameServer.server.messages;
|
package seng302.gameServer.messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by zyt10 on 10/08/17.
|
* Created by zyt10 on 10/08/17.
|
||||||
@@ -8,6 +8,8 @@ import java.util.Observable;
|
|||||||
import java.util.Observer;
|
import java.util.Observer;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||||
|
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||||
import javafx.beans.property.ReadOnlyLongProperty;
|
import javafx.beans.property.ReadOnlyLongProperty;
|
||||||
import javafx.beans.property.ReadOnlyLongWrapper;
|
import javafx.beans.property.ReadOnlyLongWrapper;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
@@ -28,19 +30,24 @@ public class ClientYacht extends Observable {
|
|||||||
Boolean sailsIn, double velocity);
|
Boolean sailsIn, double velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface MarkRoundingListener {
|
||||||
|
void notifyRounding(ClientYacht yacht, CompoundMark markPassed, int legNumber);
|
||||||
|
}
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
|
private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
|
||||||
|
|
||||||
|
|
||||||
//BOTH AFAIK
|
|
||||||
private String boatType;
|
private String boatType;
|
||||||
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;
|
||||||
private String boatName;
|
private String boatName;
|
||||||
private String country;
|
private String country;
|
||||||
|
private Integer position;
|
||||||
|
|
||||||
private Long estimateTimeAtFinish;
|
private Long estimateTimeAtFinish;
|
||||||
private Boolean sailIn = false;
|
private Boolean sailIn = true;
|
||||||
private Integer currentMarkSeqID = 0;
|
private Integer currentMarkSeqID = 0;
|
||||||
private Long markRoundTime;
|
private Long markRoundTime;
|
||||||
private Long timeTillNext;
|
private Long timeTillNext;
|
||||||
@@ -50,13 +57,13 @@ public class ClientYacht extends Observable {
|
|||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Double currentVelocity;
|
private Double currentVelocity;
|
||||||
|
|
||||||
//CLIENT SIDE
|
|
||||||
private List<YachtLocationListener> locationListeners = new ArrayList<>();
|
private List<YachtLocationListener> locationListeners = new ArrayList<>();
|
||||||
|
private List<MarkRoundingListener> markRoundingListeners = 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();
|
||||||
|
private ReadOnlyIntegerWrapper placingProperty = new ReadOnlyIntegerWrapper();
|
||||||
private CompoundMark lastMarkRounded;
|
private CompoundMark lastMarkRounded;
|
||||||
private Integer positionInt = 0;
|
|
||||||
private Color colour;
|
private Color colour;
|
||||||
|
|
||||||
public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName,
|
public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName,
|
||||||
@@ -145,12 +152,16 @@ public class ClientYacht extends Observable {
|
|||||||
this.estimateTimeAtFinish = estimateTimeAtFinish;
|
this.estimateTimeAtFinish = estimateTimeAtFinish;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getPositionInteger() {
|
public Integer getPlacing() {
|
||||||
return positionInt;
|
return placingProperty.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPositionInteger(Integer position) {
|
public void setPlacing(Integer position) {
|
||||||
this.positionInt = position;
|
placingProperty.set(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyIntegerProperty placingProperty() {
|
||||||
|
return placingProperty.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateVelocityProperty(double velocity) {
|
public void updateVelocityProperty(double velocity) {
|
||||||
@@ -189,6 +200,14 @@ public class ClientYacht extends Observable {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(Integer position) {
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
public void toggleSail() {
|
public void toggleSail() {
|
||||||
sailIn = !sailIn;
|
sailIn = !sailIn;
|
||||||
}
|
}
|
||||||
@@ -239,14 +258,6 @@ public class ClientYacht extends Observable {
|
|||||||
this.colour = colour;
|
this.colour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public Double getCurrentVelocity() {
|
|
||||||
// return currentVelocity;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void setCurrentVelocity(Double currentVelocity) {
|
|
||||||
// this.currentVelocity = currentVelocity;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
||||||
setLocation(lat, lng);
|
setLocation(lat, lng);
|
||||||
@@ -262,7 +273,25 @@ public class ClientYacht extends Observable {
|
|||||||
locationListeners.add(listener);
|
locationListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMarkRoundingListener(MarkRoundingListener listener) {
|
||||||
|
markRoundingListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeMarkRoundingListener(MarkRoundingListener listener) {
|
||||||
|
markRoundingListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getSailIn () {
|
public boolean getSailIn () {
|
||||||
return sailIn;
|
return sailIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void roundMark(CompoundMark mark, long markRoundTime, long timeSinceLastMark) {
|
||||||
|
this.markRoundTime = markRoundTime;
|
||||||
|
timeSinceLastMarkProperty.set(timeSinceLastMark);
|
||||||
|
lastMarkRounded = mark;
|
||||||
|
legNumber += 1;
|
||||||
|
for (MarkRoundingListener listener : markRoundingListeners) {
|
||||||
|
listener.notifyRounding(this, lastMarkRounded, legNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,17 @@ package seng302.model;
|
|||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Observable;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import seng302.model.stream.parser.RaceStartData;
|
import seng302.model.stream.parser.RaceStartData;
|
||||||
import seng302.model.stream.parser.RaceStatusData;
|
import seng302.model.stream.parser.RaceStatusData;
|
||||||
|
|
||||||
@@ -12,23 +22,31 @@ import seng302.model.stream.parser.RaceStatusData;
|
|||||||
*/
|
*/
|
||||||
public class RaceState {
|
public class RaceState {
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface CollisionListener {
|
||||||
|
void notifyCollision(GeoPoint location);
|
||||||
|
}
|
||||||
|
|
||||||
// private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
// private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||||
private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
|
private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
|
||||||
|
private ReadOnlyDoubleWrapper windSpeed = new ReadOnlyDoubleWrapper();
|
||||||
private double windSpeed;
|
private ReadOnlyDoubleWrapper windDirection = new ReadOnlyDoubleWrapper();
|
||||||
private double windDirection;
|
private long serverSystemTime;
|
||||||
private long raceTime;
|
|
||||||
private long expectedStartTime;
|
private long expectedStartTime;
|
||||||
private boolean isRaceStarted = false;
|
private boolean isRaceStarted = false;
|
||||||
// long timeTillStart;
|
long timeTillStart;
|
||||||
|
private ObservableList<ClientYacht> playerPositions;
|
||||||
|
private List<ClientYacht> collisions = new ArrayList<>();
|
||||||
|
private List<CollisionListener> collisionListeners = new ArrayList<>();
|
||||||
|
|
||||||
public RaceState() {
|
public RaceState() {
|
||||||
|
playerPositions = FXCollections.observableArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateState (RaceStatusData data) {
|
public void updateState (RaceStatusData data) {
|
||||||
this.windSpeed = data.getWindSpeed();
|
this.windSpeed.set(data.getWindSpeed());
|
||||||
this.windDirection = data.getWindDirection();
|
this.windDirection.set(data.getWindDirection());
|
||||||
this.raceTime = data.getCurrentTime();
|
this.serverSystemTime = data.getCurrentTime();
|
||||||
this.expectedStartTime = data.getExpectedStartTime();
|
this.expectedStartTime = data.getExpectedStartTime();
|
||||||
this.isRaceStarted = data.isRaceStarted();
|
this.isRaceStarted = data.isRaceStarted();
|
||||||
}
|
}
|
||||||
@@ -38,35 +56,67 @@ public class RaceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateState (RaceStartData data) {
|
public void updateState (RaceStartData data) {
|
||||||
// this.timeTillStart = data.getRaceStartTime();
|
this.timeTillStart = data.getRaceStartTime();
|
||||||
System.out.println(data.getRaceStartTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRaceTimeStr () {
|
public String getRaceTimeStr () {
|
||||||
return DATE_TIME_FORMAT.format(raceTime);
|
long raceTime = serverSystemTime - expectedStartTime;
|
||||||
|
if (raceTime < 0) {
|
||||||
|
return "-" + DATE_TIME_FORMAT.format(-1 * (raceTime - 1000));
|
||||||
|
} else {
|
||||||
|
return DATE_TIME_FORMAT.format(serverSystemTime - expectedStartTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeTillStart () {
|
public long getTimeTillStart () {
|
||||||
return (expectedStartTime - raceTime) / 1000;
|
return (expectedStartTime - serverSystemTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getWindSpeed() {
|
public double getWindSpeed() {
|
||||||
return windSpeed;
|
return windSpeed.doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getWindDirection() {
|
public ReadOnlyDoubleProperty windSpeedProperty() {
|
||||||
return windDirection;
|
return windSpeed.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty windDirectionProperty() {
|
||||||
|
return windDirection.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRaceTime() {
|
public long getRaceTime() {
|
||||||
return raceTime;
|
return serverSystemTime;
|
||||||
}
|
|
||||||
|
|
||||||
public long getExpectedStartTime() {
|
|
||||||
return expectedStartTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRaceStarted () {
|
public boolean isRaceStarted () {
|
||||||
return isRaceStarted;
|
return isRaceStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBoats(Collection<ClientYacht> clientYachts) {
|
||||||
|
playerPositions.setAll(clientYachts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sortPlayers() {
|
||||||
|
playerPositions.sort((yacht1, yacht2) -> Integer.compare(yacht2.getLegNumber(),
|
||||||
|
yacht1.getLegNumber()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<ClientYacht> getPlayerPositions() {
|
||||||
|
return playerPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void storeCollision(ClientYacht yacht) {
|
||||||
|
collisions.add(yacht);
|
||||||
|
for (CollisionListener collisionListener : collisionListeners) {
|
||||||
|
collisionListener.notifyCollision(yacht.getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCollisionListener(CollisionListener collisionListener) {
|
||||||
|
collisionListeners.add(collisionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCollisionListener(CollisionListener collisionListener) {
|
||||||
|
collisionListeners.remove(collisionListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import java.util.Observer;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.gameServer.server.messages.BoatStatus;
|
import seng302.gameServer.messages.BoatStatus;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ public class ServerYacht extends Observable {
|
|||||||
private Double currentVelocity;
|
private Double currentVelocity;
|
||||||
private Boolean isAuto;
|
private Boolean isAuto;
|
||||||
private Double autoHeading;
|
private Double autoHeading;
|
||||||
|
private Integer legNumber;
|
||||||
|
|
||||||
//Mark Rounding
|
//Mark Rounding
|
||||||
private Integer currentMarkSeqID;
|
private Integer currentMarkSeqID;
|
||||||
@@ -60,11 +61,12 @@ public class ServerYacht extends Observable {
|
|||||||
this.country = country;
|
this.country = country;
|
||||||
this.sailIn = false;
|
this.sailIn = false;
|
||||||
this.isAuto = false;
|
this.isAuto = false;
|
||||||
this.location = new GeoPoint(57.670341, 11.826856);
|
this.location = new GeoPoint(57.67046, 11.83751);
|
||||||
this.lastLocation = location;
|
this.lastLocation = location;
|
||||||
this.heading = 120.0; //In degrees
|
this.heading = 120.0; //In degrees
|
||||||
this.currentVelocity = 0d; //in mms-1
|
this.currentVelocity = 0d; //in mms-1
|
||||||
this.currentMarkSeqID = 0;
|
this.currentMarkSeqID = 0;
|
||||||
|
this.legNumber = 0;
|
||||||
|
|
||||||
this.hasEnteredRoundingZone = false;
|
this.hasEnteredRoundingZone = false;
|
||||||
this.hasPassedLine = false;
|
this.hasPassedLine = false;
|
||||||
@@ -381,5 +383,12 @@ public class ServerYacht extends Observable {
|
|||||||
return hasPassedLine;
|
return hasPassedLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void incrementLegNumber() {
|
||||||
|
legNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLegNumber() {
|
||||||
|
return legNumber;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package seng302.model.mark;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import seng302.gameServer.server.messages.RoundingSide;
|
import seng302.gameServer.messages.RoundingSide;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ public class CompoundMark {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoundingSide(RoundingSide roundingSide) {
|
public void setRoundingSide(RoundingSide roundingSide) {;
|
||||||
switch (roundingSide) {
|
switch (roundingSide) {
|
||||||
case SP:
|
case SP:
|
||||||
getSubMark(1).setRoundingSide(RoundingSide.STARBOARD);
|
getSubMark(1).setRoundingSide(RoundingSide.STARBOARD);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package seng302.model.mark;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import seng302.gameServer.server.messages.RoundingSide;
|
import seng302.gameServer.messages.RoundingSide;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import seng302.gameServer.server.messages.RoundingSide;
|
import seng302.gameServer.messages.RoundingSide;
|
||||||
import seng302.model.stream.xml.generator.Race;
|
import seng302.model.stream.xml.generator.Race;
|
||||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
@@ -104,10 +104,11 @@ public class MarkOrder {
|
|||||||
List<Corner> corners = data.getMarkSequence();
|
List<Corner> corners = data.getMarkSequence();
|
||||||
Map<Integer, CompoundMark> marks = data.getCompoundMarks();
|
Map<Integer, CompoundMark> marks = data.getCompoundMarks();
|
||||||
List<CompoundMark> course = new ArrayList<>();
|
List<CompoundMark> course = new ArrayList<>();
|
||||||
|
|
||||||
for (Corner corner : corners){
|
for (Corner corner : corners){
|
||||||
CompoundMark compoundMark = marks.get(corner.getCompoundMarkID());
|
CompoundMark compoundMark = marks.get(corner.getCompoundMarkID());
|
||||||
compoundMark.setRoundingSide(RoundingSide.getRoundingSide(corner.getRounding()));
|
compoundMark.setRoundingSide(
|
||||||
|
RoundingSide.getRoundingSide(corner.getRounding())
|
||||||
|
);
|
||||||
course.add(compoundMark);
|
course.add(compoundMark);
|
||||||
allMarks.addAll(compoundMark.getMarks());
|
allMarks.addAll(compoundMark.getMarks());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ public class Regatta {
|
|||||||
private Integer utcOffset;
|
private Integer utcOffset;
|
||||||
private Double magneticVariation;
|
private Double magneticVariation;
|
||||||
|
|
||||||
public Regatta(String name, Double latitude, Double longitude) {
|
public Regatta(String name, String courseName, Double latitude, Double longitude) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.id = DEFAULT_REGATTA_ID;
|
this.id = DEFAULT_REGATTA_ID;
|
||||||
this.courseName = name;
|
this.courseName = courseName;
|
||||||
|
|
||||||
this.latitude = latitude;
|
this.latitude = latitude;
|
||||||
this.longitude = longitude;
|
this.longitude = longitude;
|
||||||
|
|||||||
@@ -95,13 +95,8 @@ public class StreamParser {
|
|||||||
boatID = bytesToLong(
|
boatID = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
||||||
boatStatus = (int) payload[28 + (i * 20)];
|
boatStatus = (int) payload[28 + (i * 20)];
|
||||||
|
|
||||||
// setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
|
|
||||||
// boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
|
||||||
// boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
|
||||||
estTimeAtNextMark = bytesToLong(
|
estTimeAtNextMark = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
||||||
// boat.setEstimateTimeTillNextMark(estTimeAtNextMark);
|
|
||||||
estTimeAtFinish = bytesToLong(
|
estTimeAtFinish = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
||||||
leg = (int) payload[29 + (i * 20)];
|
leg = (int) payload[29 + (i * 20)];
|
||||||
@@ -119,12 +114,12 @@ public class StreamParser {
|
|||||||
// placing += 1;
|
// placing += 1;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// updatingBoat.setPosition(placing.toString());
|
// updatingBoat.setPlacing(placing.toString());
|
||||||
// updatingBoat.setLegNumber(leg);
|
// updatingBoat.setLegNumber(leg);
|
||||||
// boatsPos.putIfAbsent(placing, updatingBoat);
|
// boatsPos.putIfAbsent(placing, updatingBoat);
|
||||||
// boatsPos.replace(placing, updatingBoat);
|
// boatsPos.replace(placing, updatingBoat);
|
||||||
// } else if(updatingBoat.getLegNumber() == null){
|
// } else if(updatingBoat.getLegNumber() == null){
|
||||||
// updatingBoat.setPosition("1");
|
// updatingBoat.setPlacing("1");
|
||||||
// updatingBoat.setLegNumber(leg);
|
// updatingBoat.setLegNumber(leg);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import java.io.OutputStreamWriter;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import seng302.model.stream.xml.generator.Race;
|
import seng302.model.stream.xml.generator.Race;
|
||||||
import seng302.model.stream.xml.generator.Regatta;
|
import seng302.model.stream.xml.generator.Regatta;
|
||||||
import seng302.gameServer.server.messages.XMLMessageSubType;
|
import seng302.gameServer.messages.XMLMessageSubType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
|
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ import javafx.scene.control.Alert.AlertType;
|
|||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.gameServer.server.messages.BoatActionMessage;
|
import seng302.gameServer.messages.BoatActionMessage;
|
||||||
import seng302.gameServer.server.messages.ClientType;
|
import seng302.gameServer.messages.ClientType;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.gameServer.server.messages.RegistrationRequestMessage;
|
import seng302.gameServer.messages.RegistrationRequestMessage;
|
||||||
import seng302.gameServer.server.messages.RegistrationResponseStatus;
|
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||||
import seng302.model.stream.packets.PacketType;
|
import seng302.model.stream.packets.PacketType;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import javafx.fxml.FXMLLoader;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
import seng302.gameServer.MainServerThread;
|
import seng302.gameServer.MainServerThread;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
|
import seng302.gameServer.messages.BoatStatus;
|
||||||
import seng302.model.ClientYacht;
|
import seng302.model.ClientYacht;
|
||||||
import seng302.model.RaceState;
|
import seng302.model.RaceState;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
@@ -26,6 +28,7 @@ import seng302.model.stream.xml.parser.RaceXMLData;
|
|||||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||||
import seng302.utilities.StreamParser;
|
import seng302.utilities.StreamParser;
|
||||||
import seng302.utilities.XMLParser;
|
import seng302.utilities.XMLParser;
|
||||||
|
import seng302.visualiser.controllers.FinishScreenViewController;
|
||||||
import seng302.visualiser.controllers.LobbyController;
|
import seng302.visualiser.controllers.LobbyController;
|
||||||
import seng302.visualiser.controllers.LobbyController.CloseStatus;
|
import seng302.visualiser.controllers.LobbyController.CloseStatus;
|
||||||
import seng302.visualiser.controllers.RaceViewController;
|
import seng302.visualiser.controllers.RaceViewController;
|
||||||
@@ -46,6 +49,7 @@ public class GameClient {
|
|||||||
private RegattaXMLData regattaData;
|
private RegattaXMLData regattaData;
|
||||||
private RaceXMLData courseData;
|
private RaceXMLData courseData;
|
||||||
private RaceState raceState = new RaceState();
|
private RaceState raceState = new RaceState();
|
||||||
|
private LobbyController lobbyController;
|
||||||
|
|
||||||
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
|
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
|
||||||
|
|
||||||
@@ -75,8 +79,18 @@ public class GameClient {
|
|||||||
LobbyController lobbyController = loadLobby();
|
LobbyController lobbyController = loadLobby();
|
||||||
lobbyController.setPlayerListSource(clientLobbyList);
|
lobbyController.setPlayerListSource(clientLobbyList);
|
||||||
lobbyController.disableReadyButton();
|
lobbyController.disableReadyButton();
|
||||||
lobbyController.setTitle("Connected to host - IP : " + ipAddress + " Port : " + portNumber);
|
|
||||||
|
if (regattaData != null){
|
||||||
|
lobbyController.setTitle(regattaData.getRegattaName());
|
||||||
|
lobbyController.setCourseName(regattaData.getCourseName());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
lobbyController.setTitle(ipAddress);
|
||||||
|
lobbyController.setCourseName("");
|
||||||
|
}
|
||||||
|
|
||||||
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
|
lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
|
||||||
|
this.lobbyController = lobbyController;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,15 +109,27 @@ public class GameClient {
|
|||||||
socketThread.addStreamObserver(this::parsePackets);
|
socketThread.addStreamObserver(this::parsePackets);
|
||||||
LobbyController lobbyController = loadLobby();
|
LobbyController lobbyController = loadLobby();
|
||||||
lobbyController.setPlayerListSource(clientLobbyList);
|
lobbyController.setPlayerListSource(clientLobbyList);
|
||||||
lobbyController.setTitle("Hosting Lobby - IP : " + ipAddress + " Port : " + portNumber);
|
|
||||||
|
if (regattaData != null){
|
||||||
|
lobbyController.setTitle("Hosting: " + regattaData.getRegattaName());
|
||||||
|
lobbyController.setCourseName(regattaData.getCourseName());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
lobbyController.setTitle("Hosting: " + ipAddress);
|
||||||
|
lobbyController.setCourseName("");
|
||||||
|
}
|
||||||
|
|
||||||
lobbyController.addCloseListener(exitCause -> {
|
lobbyController.addCloseListener(exitCause -> {
|
||||||
if (exitCause == CloseStatus.READY) {
|
if (exitCause == CloseStatus.READY) {
|
||||||
|
GameState.resetStartTime();
|
||||||
|
lobbyController.disableReadyButton();
|
||||||
server.startGame();
|
server.startGame();
|
||||||
} else if (exitCause == CloseStatus.LEAVE) {
|
} else if (exitCause == CloseStatus.LEAVE) {
|
||||||
loadStartScreen();
|
loadStartScreen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.lobbyController = lobbyController;
|
||||||
server.setGameClient(this);
|
server.setGameClient(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,17 +167,7 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadRaceView() {
|
private void loadRaceView() {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(
|
FXMLLoader fxmlLoader = loadFXMLToHolder("/views/RaceView.fxml");
|
||||||
RaceViewController.class.getResource("/views/RaceView.fxml"));
|
|
||||||
try {
|
|
||||||
final Node node = fxmlLoader.load();
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
holderPane.getChildren().clear();
|
|
||||||
holderPane.getChildren().add(node);
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
holderPane.getScene().setOnKeyPressed(this::keyPressed);
|
holderPane.getScene().setOnKeyPressed(this::keyPressed);
|
||||||
holderPane.getScene().setOnKeyReleased(this::keyReleased);
|
holderPane.getScene().setOnKeyReleased(this::keyReleased);
|
||||||
raceView = fxmlLoader.getController();
|
raceView = fxmlLoader.getController();
|
||||||
@@ -160,17 +176,25 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadFinishScreenView() {
|
private void loadFinishScreenView() {
|
||||||
|
FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml");
|
||||||
|
FinishScreenViewController controller = fxmlLoader.getController();
|
||||||
|
controller.setFinishers(raceState.getPlayerPositions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private FXMLLoader loadFXMLToHolder(String fxmlLocation) {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(
|
FXMLLoader fxmlLoader = new FXMLLoader(
|
||||||
getClass().getResource("/views/FinishScreenView.fxml"));
|
getClass().getResource(fxmlLocation)
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
final Node finishScreenFX = fxmlLoader.load();
|
final Node fxmlLoaderFX = fxmlLoader.load();
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
holderPane.getChildren().clear();
|
holderPane.getChildren().clear();
|
||||||
holderPane.getChildren().add(finishScreenFX);
|
holderPane.getChildren().add(fxmlLoaderFX);
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
return fxmlLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parsePackets() {
|
private void parsePackets() {
|
||||||
@@ -179,13 +203,18 @@ public class GameClient {
|
|||||||
switch (packet.getType()) {
|
switch (packet.getType()) {
|
||||||
case RACE_STATUS:
|
case RACE_STATUS:
|
||||||
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
|
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
|
||||||
startRaceIfAllDataReceived();
|
|
||||||
|
if (raceState.getTimeTillStart() <= 5000) {
|
||||||
|
startRaceIfAllDataReceived();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REGATTA_XML:
|
case REGATTA_XML:
|
||||||
regattaData = XMLParser.parseRegatta(
|
regattaData = XMLParser.parseRegatta(
|
||||||
StreamParser.extractXmlMessage(packet)
|
StreamParser.extractXmlMessage(packet)
|
||||||
);
|
);
|
||||||
|
|
||||||
raceState.setTimeZone(
|
raceState.setTimeZone(
|
||||||
TimeZone.getTimeZone(
|
TimeZone.getTimeZone(
|
||||||
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
|
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
|
||||||
@@ -210,10 +239,12 @@ public class GameClient {
|
|||||||
allBoatsMap.forEach((id, boat) ->
|
allBoatsMap.forEach((id, boat) ->
|
||||||
clientLobbyList.add(id + " " + boat.getBoatName())
|
clientLobbyList.add(id + " " + boat.getBoatName())
|
||||||
);
|
);
|
||||||
|
raceState.setBoats(allBoatsMap.values());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RACE_START_STATUS:
|
case RACE_START_STATUS:
|
||||||
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
|
raceState.updateState(StreamParser.extractRaceStartStatus(packet));
|
||||||
|
if (lobbyController != null) lobbyController.updateRaceState(raceState);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOAT_LOCATION:
|
case BOAT_LOCATION:
|
||||||
@@ -247,8 +278,8 @@ public class GameClient {
|
|||||||
private void updatePosition(PositionUpdateData positionData) {
|
private void updatePosition(PositionUpdateData positionData) {
|
||||||
if (positionData.getType() == DeviceType.YACHT_TYPE) {
|
if (positionData.getType() == DeviceType.YACHT_TYPE) {
|
||||||
if (allXMLReceived() && allBoatsMap.containsKey(positionData.getDeviceId())) {
|
if (allXMLReceived() && allBoatsMap.containsKey(positionData.getDeviceId())) {
|
||||||
ClientYacht clientYacht = allBoatsMap.get(positionData.getDeviceId());
|
ClientYacht yacht = allBoatsMap.get(positionData.getDeviceId());
|
||||||
clientYacht.updateLocation(positionData.getLat(),
|
yacht.updateLocation(positionData.getLat(),
|
||||||
positionData.getLon(), positionData.getHeading(),
|
positionData.getLon(), positionData.getHeading(),
|
||||||
positionData.getGroundSpeed());
|
positionData.getGroundSpeed());
|
||||||
}
|
}
|
||||||
@@ -266,13 +297,10 @@ public class GameClient {
|
|||||||
private void updateMarkRounding(MarkRoundingData roundingData) {
|
private void updateMarkRounding(MarkRoundingData roundingData) {
|
||||||
if (allXMLReceived()) {
|
if (allXMLReceived()) {
|
||||||
ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId());
|
ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId());
|
||||||
clientYacht.setMarkRoundingTime(roundingData.getTimeStamp());
|
clientYacht.roundMark(
|
||||||
clientYacht.updateTimeSinceLastMarkProperty(
|
courseData.getCompoundMarks().get(roundingData.getMarkId()),
|
||||||
raceState.getRaceTime() - roundingData.getTimeStamp());
|
roundingData.getTimeStamp(),
|
||||||
clientYacht.setLastMarkRounded(
|
raceState.getRaceTime() - roundingData.getTimeStamp()
|
||||||
courseData.getCompoundMarks().get(
|
|
||||||
roundingData.getMarkId()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,37 +308,36 @@ public class GameClient {
|
|||||||
private void processRaceStatusUpdate(RaceStatusData data) {
|
private void processRaceStatusUpdate(RaceStatusData data) {
|
||||||
if (allXMLReceived()) {
|
if (allXMLReceived()) {
|
||||||
raceState.updateState(data);
|
raceState.updateState(data);
|
||||||
if (raceView != null) {
|
|
||||||
raceView.getGameView().setWindDir(raceState.getWindDirection());
|
|
||||||
}
|
|
||||||
boolean raceFinished = true;
|
boolean raceFinished = true;
|
||||||
for (ClientYacht yacht : allBoatsMap.values()) {
|
for (ClientYacht yacht : allBoatsMap.values()) {
|
||||||
if (yacht.getBoatStatus() != 3) {
|
if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) {
|
||||||
raceFinished = false;
|
raceFinished = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (raceFinished == true) {
|
|
||||||
close();
|
|
||||||
loadFinishScreenView();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (long[] boatData : data.getBoatData()) {
|
for (long[] boatData : data.getBoatData()) {
|
||||||
ClientYacht clientYacht = allBoatsMap.get((int) boatData[0]);
|
ClientYacht clientYacht = allBoatsMap.get((int) boatData[0]);
|
||||||
clientYacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
|
clientYacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
|
||||||
clientYacht.setEstimateTimeAtFinish(boatData[2]);
|
clientYacht.setEstimateTimeAtFinish(boatData[2]);
|
||||||
int legNumber = (int) boatData[3];
|
int legNumber = (int) boatData[3];
|
||||||
clientYacht.setLegNumber(legNumber);
|
|
||||||
clientYacht.setBoatStatus((int) boatData[4]);
|
clientYacht.setBoatStatus((int) boatData[4]);
|
||||||
if (legNumber != clientYacht.getLegNumber()) {
|
if (legNumber != clientYacht.getLegNumber()) {
|
||||||
int placing = 1;
|
clientYacht.setLegNumber(legNumber);
|
||||||
for (ClientYacht otherClientYacht : allBoatsMap.values()) {
|
updatePlayerPositions();
|
||||||
if (otherClientYacht.getSourceId() != boatData[0] &&
|
|
||||||
clientYacht.getLegNumber() <= otherClientYacht.getLegNumber())
|
|
||||||
placing++;
|
|
||||||
}
|
|
||||||
clientYacht.setPositionInteger(placing);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (raceFinished) {
|
||||||
|
close();
|
||||||
|
loadFinishScreenView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlayerPositions() {
|
||||||
|
raceState.sortPlayers();
|
||||||
|
for (ClientYacht yacht : raceState.getPlayerPositions()) {
|
||||||
|
yacht.setPosition(raceState.getPlayerPositions().indexOf(yacht) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,22 +360,16 @@ public class GameClient {
|
|||||||
socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
|
socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
|
||||||
case ENTER: // tack/gybe
|
case ENTER: // tack/gybe
|
||||||
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
|
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
|
||||||
//TODO Allow a zoom in and zoom out methods
|
|
||||||
case Z: // zoom in
|
|
||||||
System.out.println("Zoom in");
|
|
||||||
break;
|
|
||||||
case X: // zoom out
|
|
||||||
System.out.println("Zoom out");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void keyReleased(KeyEvent e) {
|
private void keyReleased(KeyEvent e) {
|
||||||
switch (e.getCode()) {
|
switch (e.getCode()) {
|
||||||
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
|
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
|
||||||
case SHIFT: // sails in/sails out
|
case SHIFT: // sails in/sails out
|
||||||
socketThread.sendBoatAction(BoatAction.SAILS_IN);
|
socketThread.sendBoatAction(BoatAction.SAILS_IN);
|
||||||
raceView.getGameView().getPlayerYacht().toggleSail();
|
allBoatsMap.get(socketThread.getClientId()).toggleSail();
|
||||||
break;
|
break;
|
||||||
case PAGE_UP:
|
case PAGE_UP:
|
||||||
case PAGE_DOWN:
|
case PAGE_DOWN:
|
||||||
@@ -366,7 +387,11 @@ public class GameClient {
|
|||||||
private void showCollisionAlert(YachtEventData yachtEventData) {
|
private void showCollisionAlert(YachtEventData yachtEventData) {
|
||||||
// 33 is the agreed code to show collision
|
// 33 is the agreed code to show collision
|
||||||
if (yachtEventData.getEventId() == 33) {
|
if (yachtEventData.getEventId() == 33) {
|
||||||
raceView.showCollision(yachtEventData.getSubjectId());
|
raceState.storeCollision(
|
||||||
|
allBoatsMap.get(
|
||||||
|
yachtEventData.getSubjectId().intValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import javafx.geometry.Point2D;
|
|||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
@@ -23,8 +25,9 @@ import javafx.scene.paint.Paint;
|
|||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import seng302.model.ClientYacht;
|
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import seng302.gameServer.messages.RoundingSide;
|
||||||
|
import seng302.model.ClientYacht;
|
||||||
import seng302.model.Colors;
|
import seng302.model.Colors;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.Limit;
|
import seng302.model.Limit;
|
||||||
@@ -36,6 +39,7 @@ import seng302.visualiser.fxObjects.AnnotationBox;
|
|||||||
import seng302.visualiser.fxObjects.BoatObject;
|
import seng302.visualiser.fxObjects.BoatObject;
|
||||||
import seng302.visualiser.fxObjects.CourseBoundary;
|
import seng302.visualiser.fxObjects.CourseBoundary;
|
||||||
import seng302.visualiser.fxObjects.Gate;
|
import seng302.visualiser.fxObjects.Gate;
|
||||||
|
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||||
import seng302.visualiser.fxObjects.Marker;
|
import seng302.visualiser.fxObjects.Marker;
|
||||||
import seng302.visualiser.map.Boundary;
|
import seng302.visualiser.map.Boundary;
|
||||||
import seng302.visualiser.map.CanvasMap;
|
import seng302.visualiser.map.CanvasMap;
|
||||||
@@ -71,11 +75,13 @@ public class GameView extends Pane {
|
|||||||
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
||||||
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
|
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
|
||||||
private ObservableList<Node> gameObjects;
|
private ObservableList<Node> gameObjects;
|
||||||
|
private BoatObject selectedBoat = null;
|
||||||
private Group annotationsGroup = new Group();
|
private Group annotationsGroup = new Group();
|
||||||
private Group wakesGroup = new Group();
|
private Group wakesGroup = new Group();
|
||||||
private Group boatObjectGroup = new Group();
|
private Group boatObjectGroup = new Group();
|
||||||
private Group trails = new Group();
|
private Group trails = new Group();
|
||||||
private Group markers = new Group();
|
private Group markers = new Group();
|
||||||
|
private List<CompoundMark> course = new ArrayList<>();
|
||||||
|
|
||||||
private ImageView mapImage = new ImageView();
|
private ImageView mapImage = new ImageView();
|
||||||
|
|
||||||
@@ -92,19 +98,19 @@ public class GameView extends Pane {
|
|||||||
|
|
||||||
double scaleFactor = 1;
|
double scaleFactor = 1;
|
||||||
|
|
||||||
public void zoomOut() {
|
private void zoomOut() {
|
||||||
scaleFactor = 0.95;
|
scaleFactor = 0.1;
|
||||||
for (Node child : getChildren()) {
|
if (this.getScaleX() > 0.5) {
|
||||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
this.setScaleX(this.getScaleX() - scaleFactor);
|
||||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
this.setScaleY(this.getScaleY() - scaleFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void zoomIn() {
|
private void zoomIn() {
|
||||||
scaleFactor = 1.05;
|
scaleFactor = 0.10;
|
||||||
for (Node child : getChildren()) {
|
if (this.getScaleX() < 2.5) {
|
||||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
this.setScaleX(this.getScaleX() + scaleFactor);
|
||||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
this.setScaleY(this.getScaleY() + scaleFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,14 +119,25 @@ public class GameView extends Pane {
|
|||||||
VERTICAL
|
VERTICAL
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameView() {
|
|
||||||
|
private void trackBoat() {
|
||||||
|
if (selectedBoat != null) {
|
||||||
|
double x = selectedBoat.getBoatLayoutX();
|
||||||
|
double y = selectedBoat.getBoatLayoutY();
|
||||||
|
double displacementX = this.getWidth();
|
||||||
|
double displacementY = this.getHeight();
|
||||||
|
this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX());
|
||||||
|
this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY());
|
||||||
|
} else {
|
||||||
|
this.setLayoutX(0);
|
||||||
|
this.setLayoutY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameView () {
|
||||||
gameObjects = this.getChildren();
|
gameObjects = this.getChildren();
|
||||||
// create image view for map, bind panel size to image
|
// create image view for map, bind panel size to image
|
||||||
gameObjects.add(mapImage);
|
gameObjects.add(mapImage);
|
||||||
fpsDisplay.setLayoutX(5);
|
|
||||||
fpsDisplay.setLayoutY(20);
|
|
||||||
fpsDisplay.setStrokeWidth(2);
|
|
||||||
gameObjects.add(fpsDisplay);
|
|
||||||
gameObjects.add(raceBorder);
|
gameObjects.add(raceBorder);
|
||||||
gameObjects.add(markers);
|
gameObjects.add(markers);
|
||||||
initializeTimer();
|
initializeTimer();
|
||||||
@@ -138,6 +155,7 @@ public class GameView extends Pane {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(long now) {
|
public void handle(long now) {
|
||||||
|
trackBoat();
|
||||||
if (lastTime == 0) {
|
if (lastTime == 0) {
|
||||||
lastTime = now;
|
lastTime = now;
|
||||||
} else {
|
} else {
|
||||||
@@ -161,9 +179,7 @@ public class GameView extends Pane {
|
|||||||
lastTime = now;
|
lastTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Platform.runLater(() ->
|
boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
|
||||||
boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -205,6 +221,7 @@ public class GameView extends Pane {
|
|||||||
mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty());
|
mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: 16/08/17 Break up this function
|
||||||
/**
|
/**
|
||||||
* Adds a course to the GameView. The view is scaled accordingly unless a border is set in which
|
* Adds a course to the GameView. The view is scaled accordingly unless a border is set in which
|
||||||
* case the course is added relative ot the border.
|
* case the course is added relative ot the border.
|
||||||
@@ -214,6 +231,23 @@ public class GameView extends Pane {
|
|||||||
*/
|
*/
|
||||||
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
||||||
markerObjects = new HashMap<>();
|
markerObjects = new HashMap<>();
|
||||||
|
|
||||||
|
for (Corner corner : sequence) { //Makes course out of all compound marks.
|
||||||
|
for (CompoundMark compoundMark : newCourse) {
|
||||||
|
if (corner.getCompoundMarkID() == compoundMark.getId()) {
|
||||||
|
course.add(compoundMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 16/08/17 Updating mark roundings here. It should not happen here. Nor should it be done this way.
|
||||||
|
for (Corner corner : sequence){
|
||||||
|
CompoundMark compoundMark = course.get(corner.getSeqID() - 1);
|
||||||
|
compoundMark.setRoundingSide(
|
||||||
|
RoundingSide.getRoundingSide(corner.getRounding())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final List<Gate> gates = new ArrayList<>();
|
final List<Gate> gates = new ArrayList<>();
|
||||||
Paint colour = Color.BLACK;
|
Paint colour = Color.BLACK;
|
||||||
//Creates new markers
|
//Creates new markers
|
||||||
@@ -228,16 +262,6 @@ public class GameView extends Pane {
|
|||||||
for (Mark mark : cMark.getMarks()) {
|
for (Mark mark : cMark.getMarks()) {
|
||||||
makeAndBindMarker(mark, colour);
|
makeAndBindMarker(mark, colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
//UNCOMMENT THIS TO HIGHLIGHT SUBMARKS 1 and 2 RED AND GREEN RESPECTIVELY FOR DEBUG
|
|
||||||
//(instead of above for loop)
|
|
||||||
// for (Mark mark : cMark.getMarks()) {
|
|
||||||
// if (mark.getSeqID() == 1) {
|
|
||||||
// makeAndBindMarker(mark, Color.RED);
|
|
||||||
// } else {
|
|
||||||
// makeAndBindMarker(mark, Color.GREEN);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//Create gate line
|
//Create gate line
|
||||||
if (cMark.isGate()) {
|
if (cMark.isGate()) {
|
||||||
for (int i = 1; i < cMark.getMarks().size(); i++) {
|
for (int i = 1; i < cMark.getMarks().size(); i++) {
|
||||||
@@ -252,6 +276,73 @@ public class GameView extends Pane {
|
|||||||
}
|
}
|
||||||
colour = Color.BLACK;
|
colour = Color.BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Creating mark arrows.
|
||||||
|
for (int i=1; i < sequence.size()-1; i++) { //General case.
|
||||||
|
double averageLat = 0;
|
||||||
|
double averageLng = 0;
|
||||||
|
int numMarks = 0;
|
||||||
|
for (Mark mark : course.get(i-1).getMarks()) {
|
||||||
|
numMarks += 1;
|
||||||
|
averageLat += mark.getLat();
|
||||||
|
averageLng += mark.getLng();
|
||||||
|
}
|
||||||
|
GeoPoint lastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||||
|
numMarks = 0;
|
||||||
|
averageLat = 0;
|
||||||
|
averageLng = 0;
|
||||||
|
for (Mark mark : course.get(i+1).getMarks()) {
|
||||||
|
numMarks += 1;
|
||||||
|
averageLat += mark.getLat();
|
||||||
|
averageLng += mark.getLng();
|
||||||
|
}
|
||||||
|
GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||||
|
// TODO: 16/08/17 This comparison is cancer and deserves to die.
|
||||||
|
for (Mark mark : course.get(i).getMarks()) {
|
||||||
|
markerObjects.get(mark).addArrows(
|
||||||
|
mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT,
|
||||||
|
GeoUtility.getBearing(lastMarkAv, mark),
|
||||||
|
GeoUtility.getBearing(mark, nextMarkAv)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 16/08/17 Make this cleaner
|
||||||
|
//First mark case
|
||||||
|
double averageLat = 0;
|
||||||
|
double averageLng = 0;
|
||||||
|
int numMarks = 0;
|
||||||
|
for (Mark mark : course.get(1).getMarks()) {
|
||||||
|
numMarks += 1;
|
||||||
|
averageLat += mark.getLat();
|
||||||
|
averageLng += mark.getLng();
|
||||||
|
}
|
||||||
|
GeoPoint firstMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||||
|
for (Mark mark : course.get(0).getMarks()) {
|
||||||
|
markerObjects.get(mark).addArrows(
|
||||||
|
mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT,
|
||||||
|
0d, //90
|
||||||
|
GeoUtility.getBearing(mark, firstMarkAv)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//Last Mark case
|
||||||
|
numMarks = 0;
|
||||||
|
averageLat = 0;
|
||||||
|
averageLng = 0;
|
||||||
|
for (Mark mark : course.get(course.size()-2).getMarks()) {
|
||||||
|
numMarks += 1;
|
||||||
|
averageLat += mark.getLat();
|
||||||
|
averageLng += mark.getLng();
|
||||||
|
}
|
||||||
|
GeoPoint secondToLastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||||
|
for (Mark mark : course.get(course.size()-1).getMarks()) {
|
||||||
|
markerObjects.get(mark).addArrows(
|
||||||
|
mark.getRoundingSide() == RoundingSide.STARBOARD ? MarkArrowFactory.RoundingSide.STARBOARD : MarkArrowFactory.RoundingSide.PORT,
|
||||||
|
GeoUtility.getBearing(secondToLastMarkAv, mark),
|
||||||
|
GeoUtility.getBearing(mark, mark)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//Scale race to markers if there is no border.
|
//Scale race to markers if there is no border.
|
||||||
if (borderPoints == null) {
|
if (borderPoints == null) {
|
||||||
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
||||||
@@ -259,8 +350,8 @@ public class GameView extends Pane {
|
|||||||
//Move the Markers to initial position.
|
//Move the Markers to initial position.
|
||||||
markerObjects.forEach(((mark, marker) -> {
|
markerObjects.forEach(((mark, marker) -> {
|
||||||
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
||||||
marker.setCenterX(p2d.getX());
|
marker.setLayoutX(p2d.getX());
|
||||||
marker.setCenterY(p2d.getY());
|
marker.setLayoutY(p2d.getY());
|
||||||
}));
|
}));
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
markers.getChildren().clear();
|
markers.getChildren().clear();
|
||||||
@@ -277,11 +368,12 @@ public class GameView extends Pane {
|
|||||||
*/
|
*/
|
||||||
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
||||||
Marker marker = new Marker(colour);
|
Marker marker = new Marker(colour);
|
||||||
|
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
|
||||||
markerObjects.put(observableMark, marker);
|
markerObjects.put(observableMark, marker);
|
||||||
observableMark.addPositionListener((mark, lat, lon) -> {
|
observableMark.addPositionListener((mark, lat, lon) -> {
|
||||||
Point2D p2d = findScaledXY(lat, lon);
|
Point2D p2d = findScaledXY(lat, lon);
|
||||||
markerObjects.get(mark).setCenterX(p2d.getX());
|
markerObjects.get(mark).setLayoutX(p2d.getX());
|
||||||
markerObjects.get(mark).setCenterY(p2d.getY());
|
markerObjects.get(mark).setLayoutY(p2d.getY());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,16 +388,16 @@ public class GameView extends Pane {
|
|||||||
private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) {
|
private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) {
|
||||||
Gate gate = new Gate(colour);
|
Gate gate = new Gate(colour);
|
||||||
gate.startXProperty().bind(
|
gate.startXProperty().bind(
|
||||||
m1.centerXProperty()
|
m1.layoutXProperty()
|
||||||
);
|
);
|
||||||
gate.startYProperty().bind(
|
gate.startYProperty().bind(
|
||||||
m1.centerYProperty()
|
m1.layoutYProperty()
|
||||||
);
|
);
|
||||||
gate.endXProperty().bind(
|
gate.endXProperty().bind(
|
||||||
m2.centerXProperty()
|
m2.layoutXProperty()
|
||||||
);
|
);
|
||||||
gate.endYProperty().bind(
|
gate.endYProperty().bind(
|
||||||
m2.centerYProperty()
|
m2.layoutYProperty()
|
||||||
);
|
);
|
||||||
return gate;
|
return gate;
|
||||||
}
|
}
|
||||||
@@ -330,6 +422,21 @@ public class GameView extends Pane {
|
|||||||
raceBorder.getPoints().setAll(boundaryPoints);
|
raceBorder.getPoints().setAll(boundaryPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: 16/08/17 initialize zooming internal to GameView only
|
||||||
|
/**
|
||||||
|
* Enables zoom. Has to be called after this is added to a scene.
|
||||||
|
*/
|
||||||
|
public void enableZoom () {
|
||||||
|
if (this.getScene() != null) {
|
||||||
|
this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
|
||||||
|
if (event.getCode() == KeyCode.Z) {
|
||||||
|
zoomIn();
|
||||||
|
} else if (event.getCode() == KeyCode.X) {
|
||||||
|
zoomOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Rescales the race to the size of the window.
|
* Rescales the race to the size of the window.
|
||||||
*
|
*
|
||||||
@@ -343,31 +450,45 @@ public class GameView extends Pane {
|
|||||||
// drawGoogleMap();
|
// drawGoogleMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setSelectedBoat(BoatObject bo, Boolean isSelected) {
|
||||||
|
if (this.selectedBoat == bo && !isSelected) {
|
||||||
|
this.selectedBoat = null;
|
||||||
|
boatObjects.forEach((boat, group) ->
|
||||||
|
group.setIsSelected(false)
|
||||||
|
);
|
||||||
|
} else if (isSelected) {
|
||||||
|
this.selectedBoat = bo;
|
||||||
|
for (BoatObject group : boatObjects.values()) {
|
||||||
|
if (group != bo) {
|
||||||
|
group.setIsSelected(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws all the boats.
|
* Draws all the boats.
|
||||||
* @param clientYachts The yachts to set in the race
|
* @param yachts The yachts to set in the race
|
||||||
*/
|
*/
|
||||||
public void setBoats(List<ClientYacht> clientYachts) {
|
public void setBoats(List<ClientYacht> yachts) {
|
||||||
BoatObject newBoat;
|
BoatObject newBoat;
|
||||||
final List<Group> wakes = new ArrayList<>();
|
final List<Group> wakes = new ArrayList<>();
|
||||||
for (ClientYacht clientYacht : clientYachts) {
|
for (ClientYacht yacht : yachts) {
|
||||||
Paint colour = Colors.getColor();
|
Paint colour = Colors.getColor();
|
||||||
newBoat = new BoatObject();
|
newBoat = new BoatObject();
|
||||||
|
newBoat.addSelectedBoatListener(this::setSelectedBoat);
|
||||||
newBoat.setFill(colour);
|
newBoat.setFill(colour);
|
||||||
boatObjects.put(clientYacht, newBoat);
|
boatObjects.put(yacht, newBoat);
|
||||||
createAndBindAnnotationBox(clientYacht, colour);
|
createAndBindAnnotationBox(yacht, colour);
|
||||||
// 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);
|
||||||
trails.getChildren().add(newBoat.getTrail());
|
trails.getChildren().add(newBoat.getTrail());
|
||||||
// TODO: 1/08/17 Make this less vile to look at.
|
// TODO: 1/08/17 Make this less vile to look at.
|
||||||
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
|
yacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
|
||||||
BoatObject bo = boatObjects.get(boat);
|
BoatObject bo = boatObjects.get(boat);
|
||||||
Point2D p2d = findScaledXY(lat, lon);
|
Point2D p2d = findScaledXY(lat, lon);
|
||||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
||||||
// annotations.get(boat).setLayoutX(p2d.getX());
|
|
||||||
// annotations.get(boat).setLayoutY(p2d.getY());
|
|
||||||
// annotations.get(boat).setLocation(100d, 100d);
|
|
||||||
annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
|
annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
|
||||||
bo.setTrajectory(
|
bo.setTrajectory(
|
||||||
heading,
|
heading,
|
||||||
@@ -385,11 +506,11 @@ public class GameView extends Pane {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) {
|
private void createAndBindAnnotationBox(ClientYacht yacht, Paint colour) {
|
||||||
AnnotationBox newAnnotation = new AnnotationBox();
|
AnnotationBox newAnnotation = new AnnotationBox();
|
||||||
newAnnotation.setFill(colour);
|
newAnnotation.setFill(colour);
|
||||||
newAnnotation.addAnnotation(
|
newAnnotation.addAnnotation(
|
||||||
"name", "Player: " + clientYacht.getShortName()
|
"name", "Player: " + yacht.getShortName()
|
||||||
);
|
);
|
||||||
// newAnnotation.addAnnotation(
|
// newAnnotation.addAnnotation(
|
||||||
// "velocity",
|
// "velocity",
|
||||||
@@ -412,7 +533,7 @@ public class GameView extends Pane {
|
|||||||
// return format.format(time);
|
// return format.format(time);
|
||||||
// }
|
// }
|
||||||
// );
|
// );
|
||||||
annotations.put(clientYacht, newAnnotation);
|
annotations.put(yacht, newAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawFps(Double fps) {
|
private void drawFps(Double fps) {
|
||||||
@@ -614,7 +735,6 @@ public class GameView extends Pane {
|
|||||||
timer.stop();
|
timer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setWindDir(double windDir) {
|
public void setWindDir(double windDir) {
|
||||||
this.windDir = windDir;
|
this.windDir = windDir;
|
||||||
}
|
}
|
||||||
@@ -627,10 +747,14 @@ public class GameView extends Pane {
|
|||||||
return playerYacht;
|
return playerYacht;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setBoatAsPlayer (ClientYacht playerYacht) {
|
public void setBoatAsPlayer (ClientYacht playerYacht) {
|
||||||
this.playerYacht = playerYacht;
|
this.playerYacht = playerYacht;
|
||||||
|
playerYacht.toggleSail();
|
||||||
boatObjects.get(playerYacht).setAsPlayer();
|
boatObjects.get(playerYacht).setAsPlayer();
|
||||||
|
CompoundMark currentMark = course.get(playerYacht.getLegNumber());
|
||||||
|
for (Mark mark : currentMark.getMarks()) {
|
||||||
|
markerObjects.get(mark).showNextExitArrow();
|
||||||
|
}
|
||||||
annotations.get(playerYacht).addAnnotation(
|
annotations.get(playerYacht).addAnnotation(
|
||||||
"velocity",
|
"velocity",
|
||||||
playerYacht.getVelocityProperty(),
|
playerYacht.getVelocityProperty(),
|
||||||
@@ -642,6 +766,31 @@ public class GameView extends Pane {
|
|||||||
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
|
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
|
||||||
gameObjects.add(annotations.get(playerYacht));
|
gameObjects.add(annotations.get(playerYacht));
|
||||||
});
|
});
|
||||||
|
playerYacht.addMarkRoundingListener(this::updateMarkArrows);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMarkArrows (ClientYacht yacht, CompoundMark compoundMark, int legNumber) {
|
||||||
|
//Only show arrows for this and next leg.
|
||||||
|
if (compoundMark != null) {
|
||||||
|
for (Mark mark : compoundMark.getMarks()) {
|
||||||
|
markerObjects.get(mark).showNextExitArrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompoundMark nextMark = null;
|
||||||
|
if (legNumber < course.size() - 1) {
|
||||||
|
nextMark = course.get(legNumber);
|
||||||
|
for (Mark mark : nextMark.getMarks()) {
|
||||||
|
markerObjects.get(mark).showNextEnterArrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (legNumber - 2 >= 0) {
|
||||||
|
CompoundMark lastMark = course.get(Math.max(0, legNumber - 2));
|
||||||
|
if (lastMark != nextMark) {
|
||||||
|
for (Mark mark : lastMark.getMarks()) {
|
||||||
|
markerObjects.get(mark).hideAllArrows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -651,32 +800,35 @@ public class GameView extends Pane {
|
|||||||
* @param collisionPoint yacht collision point
|
* @param collisionPoint yacht collision point
|
||||||
*/
|
*/
|
||||||
public void drawCollision(GeoPoint collisionPoint) {
|
public void drawCollision(GeoPoint collisionPoint) {
|
||||||
Platform.runLater(() -> {
|
Point2D point = findScaledXY(collisionPoint);
|
||||||
Point2D point = findScaledXY(collisionPoint);
|
double circleRadius = 0.0;
|
||||||
double circleRadius = 0.0;
|
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
|
||||||
Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED);
|
|
||||||
gameObjects.add(circle);
|
|
||||||
|
|
||||||
circle.setFill(Color.TRANSPARENT);
|
circle.setFill(Color.TRANSPARENT);
|
||||||
circle.setStroke(Color.RED);
|
circle.setStroke(Color.RED);
|
||||||
circle.setStrokeWidth(3);
|
circle.setStrokeWidth(3);
|
||||||
|
|
||||||
Timeline timeline = new Timeline();
|
Timeline timeline = new Timeline();
|
||||||
timeline.setCycleCount(1);
|
timeline.setCycleCount(1);
|
||||||
|
|
||||||
KeyFrame keyframe1 = new KeyFrame(Duration.ZERO,
|
KeyFrame keyframe1 = new KeyFrame(Duration.ZERO,
|
||||||
new KeyValue(circle.radiusProperty(), 0),
|
new KeyValue(circle.radiusProperty(), 0),
|
||||||
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
||||||
KeyFrame keyFrame2 = new KeyFrame(new Duration(1000),
|
KeyFrame keyFrame2 = new KeyFrame(new Duration(1000),
|
||||||
new KeyValue(circle.radiusProperty(), 50),
|
new KeyValue(circle.radiusProperty(), 50),
|
||||||
new KeyValue(circle.strokeProperty(), Color.RED));
|
new KeyValue(circle.strokeProperty(), Color.RED));
|
||||||
KeyFrame keyFrame3 = new KeyFrame(new Duration(1500),
|
KeyFrame keyFrame3 = new KeyFrame(new Duration(1500),
|
||||||
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
new KeyValue(circle.strokeProperty(), Color.TRANSPARENT));
|
||||||
|
|
||||||
timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3);
|
timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3);
|
||||||
timeline.play();
|
|
||||||
|
|
||||||
timeline.setOnFinished(event -> gameObjects.remove(circle));
|
Platform.runLater(() -> gameObjects.add(circle));
|
||||||
});
|
timeline.setOnFinished(event -> Platform.runLater(() -> gameObjects.remove(circle)));
|
||||||
|
timeline.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrameRateFXText(Text fpsDisplay) {
|
||||||
|
this.fpsDisplay = null;
|
||||||
|
this.fpsDisplay = fpsDisplay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package seng302.visualiser.controllers;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
@@ -61,9 +62,9 @@ public class FinishScreenViewController implements Initializable {
|
|||||||
finishOrderTable.refresh();
|
finishOrderTable.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFinishers(List<ClientYacht> participants) {
|
public void setFinishers(Collection<ClientYacht> participants) {
|
||||||
List<ClientYacht> sorted = new ArrayList<>(participants);
|
List<ClientYacht> sorted = new ArrayList<>(participants);
|
||||||
sorted.sort(Comparator.comparingInt(ClientYacht::getPositionInteger));
|
sorted.sort(Comparator.comparingInt(ClientYacht::getPlacing));
|
||||||
finishOrderTable.getItems().setAll(sorted);
|
finishOrderTable.getItems().setAll(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package seng302.visualiser.controllers;
|
package seng302.visualiser.controllers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
@@ -14,6 +13,8 @@ import javafx.scene.image.ImageView;
|
|||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import seng302.gameServer.GameStages;
|
import seng302.gameServer.GameStages;
|
||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
|
import seng302.model.RaceState;
|
||||||
|
import seng302.visualiser.GameClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class describing the actions of the lobby screen
|
* A class describing the actions of the lobby screen
|
||||||
@@ -67,9 +68,14 @@ public class LobbyController {
|
|||||||
private ImageView seventhImageView;
|
private ImageView seventhImageView;
|
||||||
@FXML
|
@FXML
|
||||||
private ImageView eighthImageView;
|
private ImageView eighthImageView;
|
||||||
|
@FXML
|
||||||
|
private Text timeUntilStart;
|
||||||
|
@FXML
|
||||||
|
private Text courseNameText;
|
||||||
|
|
||||||
private List<ImageView> imageViews = new ArrayList<>();
|
private List<ImageView> imageViews = new ArrayList<>();
|
||||||
private List<TextArea> listViews = new ArrayList<>();
|
private List<TextArea> listViews = new ArrayList<>();
|
||||||
|
private RaceState raceState;
|
||||||
|
|
||||||
private int MAX_NUM_PLAYERS = 8;
|
private int MAX_NUM_PLAYERS = 8;
|
||||||
|
|
||||||
@@ -89,6 +95,8 @@ public class LobbyController {
|
|||||||
fifthImageView, sixthImageView, seventhImageView, eighthImageView
|
fifthImageView, sixthImageView, seventhImageView, eighthImageView
|
||||||
);
|
);
|
||||||
initialiseImageView();
|
initialiseImageView();
|
||||||
|
|
||||||
|
timeUntilStart.setText("Waiting For Host...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +141,9 @@ public class LobbyController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void readyButtonPressed() {
|
public void readyButtonPressed() {
|
||||||
GameState.setCurrentStage(GameStages.RACING);
|
GameState.setCurrentStage(GameStages.PRE_RACE);
|
||||||
|
// Do countdown logic here
|
||||||
|
|
||||||
for (LobbyCloseListener readyListener : lobbyListeners)
|
for (LobbyCloseListener readyListener : lobbyListeners)
|
||||||
readyListener.notify(CloseStatus.READY);
|
readyListener.notify(CloseStatus.READY);
|
||||||
}
|
}
|
||||||
@@ -142,6 +152,10 @@ public class LobbyController {
|
|||||||
lobbyIpText.setText(title);
|
lobbyIpText.setText(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCourseName(String courseName){
|
||||||
|
courseNameText.setText(courseName);
|
||||||
|
}
|
||||||
|
|
||||||
public void addCloseListener(LobbyCloseListener listener) {
|
public void addCloseListener(LobbyCloseListener listener) {
|
||||||
lobbyListeners.add(listener);
|
lobbyListeners.add(listener);
|
||||||
}
|
}
|
||||||
@@ -154,6 +168,11 @@ public class LobbyController {
|
|||||||
Platform.runLater(this::updatePlayers);
|
Platform.runLater(this::updatePlayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateRaceState(RaceState raceState){
|
||||||
|
this.raceState = raceState;
|
||||||
|
timeUntilStart.setText("Starting in: " + raceState.getRaceTimeStr());
|
||||||
|
}
|
||||||
|
|
||||||
public void disableReadyButton () {
|
public void disableReadyButton () {
|
||||||
readyButton.setDisable(true);
|
readyButton.setDisable(true);
|
||||||
readyButton.setVisible(false);
|
readyButton.setVisible(false);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package seng302.visualiser.controllers;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
@@ -11,6 +10,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
@@ -71,6 +72,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
private Button selectAnnotationBtn;
|
private Button selectAnnotationBtn;
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<ClientYacht> yachtSelectionComboBox;
|
private ComboBox<ClientYacht> yachtSelectionComboBox;
|
||||||
|
@FXML
|
||||||
|
private Text fpsDisplay;
|
||||||
|
@FXML
|
||||||
|
private Text windSpeedText;
|
||||||
|
|
||||||
//Race Data
|
//Race Data
|
||||||
private Map<Integer, ClientYacht> participants;
|
private Map<Integer, ClientYacht> participants;
|
||||||
@@ -79,6 +84,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
private GameView gameView;
|
private GameView gameView;
|
||||||
private RaceState raceState;
|
private RaceState raceState;
|
||||||
|
|
||||||
|
private Timeline timerTimeline;
|
||||||
private Timer timer = new Timer();
|
private Timer timer = new Timer();
|
||||||
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
|
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
|
||||||
private ImportantAnnotationsState importantAnnotations;
|
private ImportantAnnotationsState importantAnnotations;
|
||||||
@@ -101,8 +107,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
public void loadRace (
|
public void loadRace (
|
||||||
Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState,
|
Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState,
|
||||||
ClientYacht player
|
ClientYacht player) {
|
||||||
) {
|
|
||||||
this.participants = participants;
|
this.participants = participants;
|
||||||
this.courseData = raceData;
|
this.courseData = raceData;
|
||||||
this.markers = raceData.getCompoundMarks();
|
this.markers = raceData.getCompoundMarks();
|
||||||
@@ -114,15 +120,38 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
initialiseBoatSelectionComboBox();
|
initialiseBoatSelectionComboBox();
|
||||||
initialiseSparkLine();
|
initialiseSparkLine();
|
||||||
|
|
||||||
|
raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> {
|
||||||
|
while (c.next()) {
|
||||||
|
if (c.wasPermutated()) {
|
||||||
|
updateOrder(raceState.getPlayerPositions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateOrder(raceState.getPlayerPositions());
|
||||||
gameView = new GameView();
|
gameView = new GameView();
|
||||||
Platform.runLater(() -> contentAnchorPane.getChildren().add(gameView));
|
gameView.setFrameRateFXText(fpsDisplay);
|
||||||
gameView.setBoats(new ArrayList<>(participants.values()));
|
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView));
|
||||||
gameView.updateBorder(raceData.getCourseLimit());
|
gameView.setBoats(new ArrayList<>(participants.values()));
|
||||||
gameView.updateCourse(
|
gameView.updateBorder(raceData.getCourseLimit());
|
||||||
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
|
gameView.updateCourse(
|
||||||
|
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
|
||||||
);
|
);
|
||||||
|
gameView.enableZoom();
|
||||||
gameView.setBoatAsPlayer(player);
|
gameView.setBoatAsPlayer(player);
|
||||||
gameView.startRace();
|
gameView.startRace();
|
||||||
|
|
||||||
|
raceState.addCollisionListener(gameView::drawCollision);
|
||||||
|
raceState.windDirectionProperty().addListener((obs, oldDirection, newDirection) -> {
|
||||||
|
gameView.setWindDir(newDirection.doubleValue());
|
||||||
|
updateWindDirection(newDirection.doubleValue());
|
||||||
|
});
|
||||||
|
raceState.windSpeedProperty().addListener((obs, oldSpeed, newSpeed) -> {
|
||||||
|
updateWindSpeed(newSpeed.doubleValue());
|
||||||
|
});
|
||||||
|
updateWindDirection(raceState.windDirectionProperty().doubleValue());
|
||||||
|
updateWindSpeed(raceState.getWindSpeed());
|
||||||
|
gameView.setWindDir(raceState.windDirectionProperty().doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -208,10 +237,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to add any new yachts into the race that may have started late or not have had data
|
* Used to add any new yachts into the race that may have started late or not have had data received yet
|
||||||
* received yet
|
|
||||||
*/
|
*/
|
||||||
private void updateSparkLine() {
|
private void updateSparkLine(){
|
||||||
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
|
// TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
|
||||||
// Collect the racing yachts that aren't already in the chart
|
// Collect the racing yachts that aren't already in the chart
|
||||||
sparkLineData.clear();
|
sparkLineData.clear();
|
||||||
@@ -219,40 +247,39 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
// Create a new data series for new yachts
|
// Create a new data series for new yachts
|
||||||
sparkLineCandidates
|
sparkLineCandidates
|
||||||
.stream()
|
.stream()
|
||||||
.filter(yacht -> yacht.getPositionInteger() != null)
|
.filter(yacht -> yacht.getPlacing() != null)
|
||||||
.forEach(yacht -> {
|
.forEach(yacht -> {
|
||||||
Series<String, Double> yachtData = new Series<>();
|
Series<String, Double> yachtData = new Series<>();
|
||||||
yachtData.setName(yacht.getSourceId().toString());
|
yachtData.setName(yacht.getSourceId().toString());
|
||||||
yachtData.getData().add(
|
yachtData.getData().add(
|
||||||
new XYChart.Data<>(
|
new XYChart.Data<>(
|
||||||
Integer.toString(yacht.getLegNumber()),
|
Integer.toString(yacht.getLegNumber()),
|
||||||
1.0 + participants.size() - yacht.getPositionInteger()
|
1.0 + participants.size() - yacht.getPlacing()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
sparkLineData.add(yachtData);
|
sparkLineData.add(yachtData);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
||||||
sparkLineData.sort((o1, o2) -> {
|
sparkLineData.sort((o1, o2) -> {
|
||||||
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size() - 1).getXValue());
|
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
|
||||||
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size() - 1).getXValue());
|
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
|
||||||
if (leg2 < leg1) {
|
if (leg2 < leg1){
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Adds the new data series to the sparkline (and set the colour of the series)
|
// Adds the new data series to the sparkline (and set the colour of the series)
|
||||||
Platform.runLater(() ->
|
Platform.runLater(() -> {
|
||||||
sparkLineData
|
sparkLineData
|
||||||
.stream()
|
.stream()
|
||||||
.filter(spark -> !raceSparkLine.getData().contains(spark))
|
.filter(spark -> !raceSparkLine.getData().contains(spark))
|
||||||
.forEach(spark -> {
|
.forEach(spark -> {
|
||||||
raceSparkLine.getData().add(spark);
|
raceSparkLine.getData().add(spark);
|
||||||
spark.getNode().lookup(".chart-series-line")
|
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||||
.setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
});
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseSparkLine() {
|
private void initialiseSparkLine() {
|
||||||
@@ -262,15 +289,15 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the yachts sparkline of the desired yacht and using the new leg number
|
* Updates the yachts sparkline of the desired yacht and using the new leg number
|
||||||
* @param clientYacht The yacht to be updated on the sparkline
|
* @param yacht The yacht to be updated on the sparkline
|
||||||
* @param legNumber the leg number that the position will be assigned to
|
* @param legNumber the leg number that the position will be assigned to
|
||||||
*/
|
*/
|
||||||
void updateYachtPositionSparkline(ClientYacht clientYacht, Integer legNumber) {
|
void updateYachtPositionSparkline(ClientYacht yacht, Integer legNumber){
|
||||||
for (XYChart.Series<String, Double> positionData : sparkLineData) {
|
for (XYChart.Series<String, Double> positionData : sparkLineData) {
|
||||||
positionData.getData().add(
|
positionData.getData().add(
|
||||||
new Data<>(
|
new Data<>(
|
||||||
Integer.toString(legNumber),
|
Integer.toString(legNumber),
|
||||||
1.0 + participants.size() - clientYacht.getPositionInteger()
|
1.0 + participants.size() - yacht.getPlacing()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -278,7 +305,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
// positionData.getData().add(
|
// positionData.getData().add(
|
||||||
// new XYChart.Data<>(
|
// new XYChart.Data<>(
|
||||||
// Integer.toString(legNumber),
|
// Integer.toString(legNumber),
|
||||||
// 1.0 + participants.size() - yacht.getPosition()
|
// 1.0 + participants.size() - yacht.getPlacing()
|
||||||
// )
|
// )
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
@@ -286,52 +313,47 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the rgb string of the yachts colour to use for the chart via css
|
* gets the rgb string of the yachts colour to use for the chart via css
|
||||||
*
|
|
||||||
* @param yachtId id of yacht passed in to get the yachts colour
|
* @param yachtId id of yacht passed in to get the yachts colour
|
||||||
* @return the colour as an rgb string
|
* @return the colour as an rgb string
|
||||||
*/
|
*/
|
||||||
private String getBoatColorAsRGB(String yachtId) {
|
private String getBoatColorAsRGB(String yachtId){
|
||||||
Color color = participants.get(Integer.valueOf(yachtId)).getColour();
|
Color color = participants.get(Integer.valueOf(yachtId)).getColour();
|
||||||
if (color == null) {
|
if (color == null){
|
||||||
return String.format("#%02X%02X%02X", 255, 255, 255);
|
return String.format("#%02X%02X%02X",255,255,255);
|
||||||
}
|
}
|
||||||
return String.format("#%02X%02X%02X",
|
return String.format( "#%02X%02X%02X",
|
||||||
(int) (color.getRed() * 255),
|
(int)( color.getRed() * 255 ),
|
||||||
(int) (color.getGreen() * 255),
|
(int)( color.getGreen() * 255 ),
|
||||||
(int) (color.getBlue() * 255)
|
(int)( color.getBlue() * 255 )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht
|
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht
|
||||||
* orderings etc.. which are dependent on the info from the stream parser constantly. Updates of
|
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
||||||
* each of these attributes are called ONCE EACH SECOND
|
* Updates of each of these attributes are called ONCE EACH SECOND
|
||||||
*/
|
*/
|
||||||
private void initializeUpdateTimer() {
|
private void initializeUpdateTimer() {
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
updateRaceTime();
|
updateRaceTime();
|
||||||
updateWindDirection();
|
|
||||||
updateOrder();
|
|
||||||
// updateSparkLine();
|
|
||||||
}
|
}
|
||||||
}, 0, 1000);
|
}, 0, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all corners until ones SeqID matches with the yachts current leg number. Then
|
* Iterates over all corners until ones SeqID matches with the yachts current leg number.
|
||||||
* it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark Returns
|
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
|
||||||
* null if no next mark found.
|
* Returns null if no next mark found.
|
||||||
*
|
|
||||||
* @param bg The BoatGroup to find the next mark of
|
* @param bg The BoatGroup to find the next mark of
|
||||||
* @return The next Mark or null if none found
|
* @return The next Mark or null if none found
|
||||||
*/
|
*/
|
||||||
private Mark getNextMark(BoatObject bg) {
|
private Mark getNextMark(BoatObject bg) {
|
||||||
// TODO: 1/08/17 Move to GameView
|
// TODO: 1/08/17 Move to GameView
|
||||||
//
|
//
|
||||||
// Integer legNumber = bg.getYacht().getLegNumber();
|
// Integer legNumber = bg.getClientYacht().getLegNumber();
|
||||||
// List<Corner> markSequence = courseData.getMarkSequence();
|
// List<Corner> markSequence = courseData.getMarkSequence();
|
||||||
//
|
//
|
||||||
// if (legNumber == 0) {
|
// if (legNumber == 0) {
|
||||||
@@ -352,10 +374,19 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the wind direction arrow and text as from info from the StreamParser
|
* Updates the wind direction arrow and text as from info from the StreamParser
|
||||||
|
* @param direction the from north angle of the wind.
|
||||||
*/
|
*/
|
||||||
private void updateWindDirection() {
|
private void updateWindDirection(double direction) {
|
||||||
windDirectionText.setText(String.format("%.1f°", raceState.getWindDirection()));
|
windDirectionText.setText(String.format("%.1f°", direction));
|
||||||
windArrowText.setRotate(raceState.getWindDirection());
|
windArrowText.setRotate(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the speed of the wind as displayed by the info pane.
|
||||||
|
* @param windSpeed Windspeed in knots.
|
||||||
|
*/
|
||||||
|
private void updateWindSpeed(double windSpeed) {
|
||||||
|
windSpeedText.setText("Speed: " + String.format("%.1f", windSpeed) + " Knots");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -363,38 +394,32 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
* Updates the clock for the race
|
* Updates the clock for the race
|
||||||
*/
|
*/
|
||||||
private void updateRaceTime() {
|
private void updateRaceTime() {
|
||||||
if (!raceState.isRaceStarted()) {
|
// if (!raceState.isRaceStarted()) {
|
||||||
timerLabel.setFill(Color.RED);
|
// timerLabel.setFill(Color.RED);
|
||||||
timerLabel.setText("Race Finished!");
|
// timerLabel.setText("Race Finished!");
|
||||||
} else {
|
// } else {
|
||||||
timerLabel.setText(raceState.getRaceTimeStr());
|
timerLabel.setText(raceState.getRaceTimeStr());
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the order of the yachts as from the StreamParser and sets them in the yacht order
|
* Updates the order of the yachts as from the StreamParser and sets them in the yacht order
|
||||||
* section
|
* section
|
||||||
*/
|
*/
|
||||||
private void updateOrder() {
|
private void updateOrder(ObservableList<ClientYacht> yachts) {
|
||||||
// positionVbox.getChildren().removeAll();
|
|
||||||
// positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
|
||||||
|
|
||||||
// list of racing yacht id
|
|
||||||
List<ClientYacht> sorted = new ArrayList<>(participants.values());
|
|
||||||
sorted.sort(Comparator.comparingInt(ClientYacht::getPositionInteger));
|
|
||||||
List<Text> vboxEntries = new ArrayList<>();
|
List<Text> vboxEntries = new ArrayList<>();
|
||||||
|
|
||||||
for (ClientYacht clientYacht : sorted) {
|
for (int i = 0; i < yachts.size(); i++) {
|
||||||
// System.out.println("yacht == null " + String.valueOf(yacht == null));
|
// System.out.println("yacht == null " + String.valueOf(yacht == null));
|
||||||
if (clientYacht.getBoatStatus() == 3) { // 3 is finish status
|
if (yachts.get(i).getBoatStatus() == 3) { // 3 is finish status
|
||||||
Text textToAdd = new Text(clientYacht.getPositionInteger() + ". " +
|
Text textToAdd = new Text(i + 1 + ". " +
|
||||||
clientYacht.getShortName() + " (Finished)");
|
yachts.get(i).getShortName() + " (Finished)");
|
||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
vboxEntries.add(textToAdd);
|
vboxEntries.add(textToAdd);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Text textToAdd = new Text(clientYacht.getPositionInteger() + ". " +
|
Text textToAdd = new Text(i + 1 + ". " +
|
||||||
clientYacht.getShortName() + " ");
|
yachts.get(i).getShortName() + " ");
|
||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
textToAdd.setStyle("");
|
textToAdd.setStyle("");
|
||||||
vboxEntries.add(textToAdd);
|
vboxEntries.add(textToAdd);
|
||||||
@@ -403,13 +428,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
Platform.runLater(() ->
|
Platform.runLater(() ->
|
||||||
positionVbox.getChildren().setAll(vboxEntries)
|
positionVbox.getChildren().setAll(vboxEntries)
|
||||||
);
|
);
|
||||||
// participants.forEach((id, yacht) ->{
|
|
||||||
// Text textToAdd = new Text(yacht.getPosition() + ". " +
|
|
||||||
// yacht.getShortName() + " ");
|
|
||||||
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
|
||||||
// textToAdd.setStyle("");
|
|
||||||
// positionVbox.getChildren().add(textToAdd);
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -488,8 +506,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
||||||
|
|
||||||
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
|
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
|
||||||
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
|
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
|
||||||
return line;
|
return line;
|
||||||
@@ -507,8 +524,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialised the combo box with any yachts currently in the race and adds the required
|
* Initialised the combo box with any yachts currently in the race and adds the required listener
|
||||||
* listener for the combobox to take action upon selection
|
* for the combobox to take action upon selection
|
||||||
*/
|
*/
|
||||||
private void initialiseBoatSelectionComboBox() {
|
private void initialiseBoatSelectionComboBox() {
|
||||||
yachtSelectionComboBox.setItems(
|
yachtSelectionComboBox.setItems(
|
||||||
@@ -580,9 +597,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
/**
|
/**
|
||||||
* Sets all the annotations of the selected yacht to be visible and all others to be hidden
|
* Sets all the annotations of the selected yacht to be visible and all others to be hidden
|
||||||
*
|
*
|
||||||
* @param clientYacht The yacht for which we want to view all annotations
|
* @param yacht The yacht for which we want to view all annotations
|
||||||
*/
|
*/
|
||||||
private void setSelectedBoat(ClientYacht clientYacht) {
|
private void setSelectedBoat(ClientYacht yacht) {
|
||||||
// for (BoatObject bg : gameViewController.getBoatGroups()) {
|
// for (BoatObject bg : gameViewController.getBoatGroups()) {
|
||||||
// //We need to iterate over all race groups to get the matching yacht group belonging to this yacht if we
|
// //We need to iterate over all race groups to get the matching yacht group belonging to this yacht if we
|
||||||
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its yachtgroup.
|
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its yachtgroup.
|
||||||
@@ -596,23 +613,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateRaceData(RaceXMLData raceData) {
|
public void updateRaceData (RaceXMLData raceData) {
|
||||||
this.courseData = raceData;
|
this.courseData = raceData;
|
||||||
gameView.updateBorder(raceData.getCourseLimit());
|
gameView.updateBorder(raceData.getCourseLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by game client after receiving yacht event packet. Parameter subject id is the
|
|
||||||
* offending yacht. This function in turn will pass the yacht location to game view to display a
|
|
||||||
* collision alert.
|
|
||||||
*
|
|
||||||
* @param subjectId source id of offending yacht
|
|
||||||
*/
|
|
||||||
public void showCollision(Long subjectId) {
|
|
||||||
gameView.drawCollision(participants.get((int) (long) subjectId).getLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameView getGameView() {
|
|
||||||
return gameView;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ public class AnnotationBox extends Group {
|
|||||||
background.setStroke(theme);
|
background.setStroke(theme);
|
||||||
background.setStrokeWidth(2);
|
background.setStrokeWidth(2);
|
||||||
background.setCache(true);
|
background.setCache(true);
|
||||||
background.setCacheHint(CacheHint.SPEED);
|
background.setCacheHint(CacheHint.SCALE);
|
||||||
this.getChildren().add(background);
|
this.getChildren().add(background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ public class AnnotationBox extends Group {
|
|||||||
Text text = new Text();
|
Text text = new Text();
|
||||||
text.setFill(theme);
|
text.setFill(theme);
|
||||||
text.setStrokeWidth(2);
|
text.setStrokeWidth(2);
|
||||||
text.setCacheHint(CacheHint.SPEED);
|
// text.setCacheHint(CacheHint.QUALITY);
|
||||||
text.setCache(true);
|
text.setCache(true);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package seng302.visualiser.fxObjects;
|
package seng302.visualiser.fxObjects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.CacheHint;
|
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
@@ -11,6 +11,7 @@ import javafx.scene.paint.Paint;
|
|||||||
import javafx.scene.shape.Line;
|
import javafx.scene.shape.Line;
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.shape.Polyline;
|
import javafx.scene.shape.Polyline;
|
||||||
|
import javafx.scene.shape.StrokeLineCap;
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,6 +24,12 @@ import javafx.scene.transform.Rotate;
|
|||||||
*/
|
*/
|
||||||
public class BoatObject extends Group {
|
public class BoatObject extends Group {
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SelectedBoatListener {
|
||||||
|
|
||||||
|
void notifySelected(BoatObject boatObject, Boolean isSelected);
|
||||||
|
}
|
||||||
|
|
||||||
//Constants for drawing
|
//Constants for drawing
|
||||||
private static final double BOAT_HEIGHT = 15d;
|
private static final double BOAT_HEIGHT = 15d;
|
||||||
private static final double BOAT_WIDTH = 10d;
|
private static final double BOAT_WIDTH = 10d;
|
||||||
@@ -41,9 +48,11 @@ public class BoatObject extends Group {
|
|||||||
private double distanceTravelled, lastRotation;
|
private double distanceTravelled, lastRotation;
|
||||||
private Point2D lastPoint;
|
private Point2D lastPoint;
|
||||||
private Paint colour = Color.BLACK;
|
private Paint colour = Color.BLACK;
|
||||||
private Boolean isSelected, destinationSet; //All boats are initialised as selected
|
private Boolean isSelected = false, destinationSet; //All boats are initialised as selected
|
||||||
private boolean isPlayer = false;
|
private boolean isPlayer = false;
|
||||||
|
|
||||||
|
private List<SelectedBoatListener> selectedBoatListenerListeners = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a BoatGroup with the default triangular boat polygon.
|
* Creates a BoatGroup with the default triangular boat polygon.
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +94,7 @@ public class BoatObject extends Group {
|
|||||||
});
|
});
|
||||||
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
|
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
|
||||||
boatPoly.setCache(true);
|
boatPoly.setCache(true);
|
||||||
boatPoly.setCacheHint(CacheHint.SPEED);
|
// boatPoly.setCacheHint(CacheHint.SPEED);
|
||||||
|
|
||||||
// annotationBox = new AnnotationBox();
|
// annotationBox = new AnnotationBox();
|
||||||
// annotationBox.setFill(colour);
|
// annotationBox.setFill(colour);
|
||||||
@@ -287,6 +296,7 @@ public class BoatObject extends Group {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
public void setIsSelected(Boolean isSelected) {
|
public void setIsSelected(Boolean isSelected) {
|
||||||
|
updateListener(isSelected);
|
||||||
this.isSelected = isSelected;
|
this.isSelected = isSelected;
|
||||||
setLineGroupVisible(isSelected);
|
setLineGroupVisible(isSelected);
|
||||||
setWakeVisible(isSelected);
|
setWakeVisible(isSelected);
|
||||||
@@ -352,7 +362,8 @@ public class BoatObject extends Group {
|
|||||||
BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75
|
BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75
|
||||||
);
|
);
|
||||||
boatPoly.setStroke(Color.BLACK);
|
boatPoly.setStroke(Color.BLACK);
|
||||||
boatPoly.setStrokeWidth(3);
|
boatPoly.setStrokeWidth(2);
|
||||||
|
boatPoly.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
isPlayer = true;
|
isPlayer = true;
|
||||||
animateSail();
|
animateSail();
|
||||||
}
|
}
|
||||||
@@ -365,6 +376,10 @@ public class BoatObject extends Group {
|
|||||||
lastHeading = heading;
|
lastHeading = heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getSelected() {
|
||||||
|
return isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTrajectory(double heading, double velocity, double scaleFactorX, double scaleFactorY) {
|
public void setTrajectory(double heading, double velocity, double scaleFactorX, double scaleFactorY) {
|
||||||
// wake.setRotation(lastHeading - heading, velocity);
|
// wake.setRotation(lastHeading - heading, velocity);
|
||||||
// rotateTo(heading);
|
// rotateTo(heading);
|
||||||
@@ -372,4 +387,14 @@ public class BoatObject extends Group {
|
|||||||
// yVelocity = Math.sin(Math.toRadians(heading)) * velocity * scaleFactorY;
|
// yVelocity = Math.sin(Math.toRadians(heading)) * velocity * scaleFactorY;
|
||||||
lastHeading = heading;
|
lastHeading = heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateListener(Boolean isSelected) {
|
||||||
|
for (SelectedBoatListener sbl : selectedBoatListenerListeners) {
|
||||||
|
sbl.notifySelected(this, isSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSelectedBoatListener(SelectedBoatListener sbl) {
|
||||||
|
selectedBoatListenerListeners.add(sbl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package seng302.visualiser.fxObjects;
|
||||||
|
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.Paint;
|
||||||
|
import javafx.scene.shape.*;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
|
// TODO: 16/08/17 this class used to be well written... FeelsBadMan. Maybe lose the ternary operators.
|
||||||
|
/**
|
||||||
|
* Factory class for making rounding arrows for mark objects out of JavaFX objects.
|
||||||
|
*/
|
||||||
|
public class MarkArrowFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The side of the boat that will be closest to the mark.
|
||||||
|
*/
|
||||||
|
public enum RoundingSide {
|
||||||
|
PORT,
|
||||||
|
STARBOARD,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final double MARK_ARROW_SEPARATION = 15;
|
||||||
|
public static final double ARROW_LENGTH = 75;
|
||||||
|
public static final double ARROW_HEAD_DEPTH = 10;
|
||||||
|
public static final double ARROW_HEAD_WIDTH = 6;
|
||||||
|
public static final double STROKE_WIDTH = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an entry arrow group showing an arrow into and out of the rounding area. It is centered on (0, 0).
|
||||||
|
* @param roundingSide The side of the boat that will be closest to the mark.
|
||||||
|
* @param angleOfEntry The angle between this mark and the last one as a heading from north in degrees.
|
||||||
|
* @param angleOfExit The angle between this mark and the next one as a heading from north in degrees.
|
||||||
|
* @param colour The desired colour of the arrows.
|
||||||
|
* @return The group containing all JavaFX objects.
|
||||||
|
*/
|
||||||
|
public static Group constructEntryArrow (RoundingSide roundingSide, double angleOfEntry,
|
||||||
|
double angleOfExit, Paint colour) {
|
||||||
|
if (roundingSide == RoundingSide.PORT && angleOfEntry < angleOfExit && Math.abs(angleOfExit - angleOfEntry) < 180) {
|
||||||
|
return makeInteriorAngle(roundingSide, angleOfExit, angleOfEntry, colour);
|
||||||
|
} else if (roundingSide == RoundingSide.STARBOARD && angleOfEntry > angleOfExit && -Math.abs(angleOfEntry - angleOfExit) > -180) {
|
||||||
|
return makeInteriorAngle(roundingSide, angleOfExit, angleOfEntry, colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
angleOfEntry = 180 - angleOfEntry;
|
||||||
|
Group arrow = new Group();
|
||||||
|
Group exitSection = constructExitArrow(roundingSide, angleOfExit, colour);
|
||||||
|
angleOfExit = 180 - angleOfExit;
|
||||||
|
Arc roundSection = new Arc(
|
||||||
|
0, 0, MARK_ARROW_SEPARATION, MARK_ARROW_SEPARATION,
|
||||||
|
(roundingSide == RoundingSide.PORT ? -180 : 0) + angleOfEntry,
|
||||||
|
roundingSide == RoundingSide.PORT ? Math.abs(angleOfExit - angleOfEntry) : -Math.abs(angleOfEntry - angleOfExit)
|
||||||
|
);
|
||||||
|
roundSection.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
roundSection.setType(ArcType.OPEN);
|
||||||
|
roundSection.setStroke(colour);
|
||||||
|
roundSection.setFill(new Color(0,0,0,0));
|
||||||
|
Polygon entrySection = constructLineSegment(
|
||||||
|
roundingSide == RoundingSide.PORT ? RoundingSide.STARBOARD : RoundingSide.PORT, 180 + angleOfEntry, colour
|
||||||
|
);
|
||||||
|
arrow.getChildren().addAll(exitSection, roundSection, entrySection);
|
||||||
|
return arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Group makeInteriorAngle (RoundingSide roundingSide, double angleOfExit, double angleOfEntry, Paint colour) {
|
||||||
|
Group arrow = new Group();
|
||||||
|
Polygon lineSegment;
|
||||||
|
angleOfEntry = Math.toRadians(360 - angleOfEntry);
|
||||||
|
angleOfExit = Math.toRadians(180 - angleOfExit);
|
||||||
|
int multiplier = roundingSide == RoundingSide.STARBOARD ? -1 : 1;
|
||||||
|
double xStart = multiplier * MARK_ARROW_SEPARATION * Math.sin(angleOfEntry + Math.PI / 2);
|
||||||
|
double yStart = multiplier * MARK_ARROW_SEPARATION * Math.cos(angleOfEntry + Math.PI / 2);
|
||||||
|
xStart = xStart + (ARROW_LENGTH * Math.sin(angleOfEntry));
|
||||||
|
yStart = yStart + (ARROW_LENGTH * Math.cos(angleOfEntry));
|
||||||
|
multiplier = roundingSide == RoundingSide.STARBOARD ? 1 : -1;
|
||||||
|
double xEnd = multiplier * MARK_ARROW_SEPARATION * Math.sin(angleOfExit + Math.PI / 2);
|
||||||
|
double yEnd = multiplier * MARK_ARROW_SEPARATION * Math.cos(angleOfExit + Math.PI / 2);
|
||||||
|
xEnd = xEnd + (ARROW_LENGTH * Math.sin(angleOfExit));
|
||||||
|
yEnd = yEnd + (ARROW_LENGTH * Math.cos(angleOfExit));
|
||||||
|
lineSegment = new Polygon(
|
||||||
|
xStart, yStart,
|
||||||
|
xEnd, yEnd
|
||||||
|
);
|
||||||
|
lineSegment.setStroke(colour);
|
||||||
|
lineSegment.setFill(Color.BLUE);
|
||||||
|
lineSegment.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
lineSegment.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
Polyline arrowHead = constructArrowHead(
|
||||||
|
90 + Math.toDegrees(Math.atan2(yStart - yEnd, xEnd - xStart)),
|
||||||
|
colour
|
||||||
|
);
|
||||||
|
arrowHead.setLayoutX(xEnd);
|
||||||
|
arrowHead.setLayoutY(yEnd);
|
||||||
|
arrow.getChildren().addAll(lineSegment, arrowHead);
|
||||||
|
return arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an exit arrow group pointing towards the next mark.
|
||||||
|
* @param roundingSide The side of the boat that will be closest to the mark.
|
||||||
|
* @param angle The angle to the next mark as a heading from north in degrees.
|
||||||
|
* @param colour The colour of the arrow.
|
||||||
|
* @return The group containing all the JavaFX objects.
|
||||||
|
*/
|
||||||
|
public static Group constructExitArrow (RoundingSide roundingSide, double angle, Paint colour) {
|
||||||
|
angle = 180 - angle;
|
||||||
|
Group arrow = new Group();
|
||||||
|
Polygon arrowBody = constructLineSegment(roundingSide, angle, colour);
|
||||||
|
Polyline arrowHead = constructArrowHead(angle, colour);
|
||||||
|
arrowHead.setLayoutX(arrowBody.getPoints().get(2));
|
||||||
|
arrowHead.setLayoutY(arrowBody.getPoints().get(3));
|
||||||
|
arrow.getChildren().addAll(arrowBody, arrowHead);
|
||||||
|
return arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Polygon constructLineSegment (RoundingSide roundingSide, double angle, Paint colour) {
|
||||||
|
Polygon lineSegment;
|
||||||
|
angle = Math.toRadians(angle);
|
||||||
|
int multiplier = roundingSide == RoundingSide.STARBOARD ? 1 : -1;
|
||||||
|
double xStart = multiplier * MARK_ARROW_SEPARATION * Math.sin(angle + Math.PI / 2);
|
||||||
|
double yStart = multiplier * MARK_ARROW_SEPARATION * Math.cos(angle + Math.PI / 2);
|
||||||
|
double xEnd = xStart + (ARROW_LENGTH * Math.sin(angle));
|
||||||
|
double yEnd = yStart + (ARROW_LENGTH * Math.cos(angle));
|
||||||
|
lineSegment = new Polygon(
|
||||||
|
xStart, yStart,
|
||||||
|
xEnd, yEnd
|
||||||
|
);
|
||||||
|
lineSegment.setStroke(colour);
|
||||||
|
lineSegment.setFill(Color.BLUE);
|
||||||
|
lineSegment.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
lineSegment.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
return lineSegment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Polyline constructArrowHead (double rotation, Paint colour) {
|
||||||
|
Polyline arrow = new Polyline(
|
||||||
|
-ARROW_HEAD_WIDTH, -ARROW_HEAD_DEPTH,
|
||||||
|
0, 0,
|
||||||
|
ARROW_HEAD_WIDTH, -ARROW_HEAD_DEPTH
|
||||||
|
);
|
||||||
|
arrow.getTransforms().add(new Rotate(-rotation));
|
||||||
|
arrow.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
arrow.setStroke(colour);
|
||||||
|
arrow.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
return arrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,98 @@
|
|||||||
package seng302.visualiser.fxObjects;
|
package seng302.visualiser.fxObjects;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visual object for a mark.
|
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
||||||
*/
|
*/
|
||||||
public class Marker extends Circle {
|
public class Marker extends Group {
|
||||||
|
|
||||||
|
private Circle mark = new Circle();
|
||||||
|
private Paint colour = Color.BLACK;
|
||||||
|
private List<Group> enterArrows = new ArrayList<>();
|
||||||
|
private List<Group> exitArrows = new ArrayList<>();
|
||||||
|
private int enterArrowIndex = 0;
|
||||||
|
private int exitArrowIndex = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Marker containing only a circle. The default colour is black.
|
||||||
|
*/
|
||||||
public Marker() {
|
public Marker() {
|
||||||
super.setRadius(5);
|
mark.setRadius(5);
|
||||||
|
mark.setCenterX(0);
|
||||||
|
mark.setCenterY(0);
|
||||||
|
Platform.runLater(() -> this.getChildren().addAll(mark, new Group())); //Empty group placeholder or arrows.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Marker containing only a circle of the given colour.
|
||||||
|
* @param colour the desired colour for the marker.
|
||||||
|
*/
|
||||||
public Marker(Paint colour) {
|
public Marker(Paint colour) {
|
||||||
this();
|
this();
|
||||||
super.setFill(colour);
|
this.colour = colour;
|
||||||
|
mark.setFill(colour);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an exit and entry arrow pair to the mark. Arrows are hidden and shown in the order they
|
||||||
|
* are created by calling showNextEnterArrow() or showNextExitArrow()
|
||||||
|
* @param roundingSide the side the marker will be from the perspective of the arrow.
|
||||||
|
* @param entryAngle The angle the arrow will point towards a marker
|
||||||
|
* @param exitAngle The angle the arrow wil point from the marker.
|
||||||
|
*/
|
||||||
|
public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle,
|
||||||
|
double exitAngle) {
|
||||||
|
|
||||||
|
enterArrows.add(
|
||||||
|
MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, colour)
|
||||||
|
);
|
||||||
|
exitArrows.add(
|
||||||
|
MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, colour)
|
||||||
|
);
|
||||||
|
// Platform.runLater(() -> {
|
||||||
|
// this.getChildren().add(enterArrows.get(enterArrows.size()-1));
|
||||||
|
// this.getChildren().add(exitArrows.get(exitArrows.size()-1));
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
||||||
|
*/
|
||||||
|
public void showNextEnterArrow() {
|
||||||
|
showArrow(enterArrows, enterArrowIndex);
|
||||||
|
enterArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
||||||
|
*/
|
||||||
|
public void showNextExitArrow() {
|
||||||
|
showArrow(exitArrows, exitArrowIndex);
|
||||||
|
exitArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showArrow(List<Group> arrowList, int arrowListIndex) {
|
||||||
|
if (arrowListIndex < arrowList.size()) {
|
||||||
|
if (arrowListIndex == 1) {;
|
||||||
|
}
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
this.getChildren().remove(1);
|
||||||
|
this.getChildren().add(arrowList.get(arrowListIndex));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides all arrows.
|
||||||
|
*/
|
||||||
|
public void hideAllArrows() {
|
||||||
|
Platform.runLater(() -> this.getChildren().setAll(mark, new Group()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.671629" TargetLng="11.840951" SourceID="125" />
|
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.671629" TargetLng="11.840951" SourceID="125" />
|
||||||
</CompoundMark>
|
</CompoundMark>
|
||||||
<CompoundMark CompoundMarkID="4" Name="Mark3">
|
<CompoundMark CompoundMarkID="4" Name="Mark3">
|
||||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.665316" TargetLng="11.827184" SourceID="126" />
|
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.664190" TargetLng="11.829576" SourceID="126" />
|
||||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.664190" TargetLng="11.829576" SourceID="127" />
|
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.665316" TargetLng="11.827184" SourceID="127" />
|
||||||
</CompoundMark>
|
</CompoundMark>
|
||||||
<CompoundMark CompoundMarkID="5" Name="Mark4">
|
<CompoundMark CompoundMarkID="5" Name="Mark4">
|
||||||
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.672350" TargetLng="11.842535" SourceID="128" />
|
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.672350" TargetLng="11.842535" SourceID="128" />
|
||||||
@@ -38,12 +38,7 @@
|
|||||||
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
||||||
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
||||||
<Corner SeqID="5" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
<Corner SeqID="5" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
||||||
<Corner SeqID="6" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
<Corner SeqID="6" CompoundMarkID="5" Rounding="PS" ZoneSize="3" />
|
||||||
<Corner SeqID="7" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
|
||||||
<Corner SeqID="8" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
|
||||||
<Corner SeqID="9" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
|
||||||
<Corner SeqID="10" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
|
||||||
<Corner SeqID="11" CompoundMarkID="5" Rounding="PS" ZoneSize="3" />
|
|
||||||
</CompoundMarkSequence>
|
</CompoundMarkSequence>
|
||||||
<CourseLimit>
|
<CourseLimit>
|
||||||
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100" />
|
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100" />
|
||||||
|
|||||||
@@ -27,11 +27,6 @@
|
|||||||
<RowConstraints maxHeight="100.0" minHeight="0.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="100.0" minHeight="0.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Text fx:id="lobbyIpText" fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Lobby: IP" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER">
|
|
||||||
<font>
|
|
||||||
<Font size="29.0" />
|
|
||||||
</font>
|
|
||||||
</Text>
|
|
||||||
<GridPane prefHeight="166.0" prefWidth="1530.0" GridPane.rowIndex="2">
|
<GridPane prefHeight="166.0" prefWidth="1530.0" GridPane.rowIndex="2">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
@@ -109,5 +104,30 @@
|
|||||||
</ImageView>
|
</ImageView>
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
<GridPane>
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="134.0" minHeight="10.0" prefHeight="32.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="134.0" minHeight="10.0" prefHeight="35.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="134.0" minHeight="10.0" prefHeight="32.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="79.0" minHeight="10.0" prefHeight="52.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="79.0" minHeight="10.0" prefHeight="35.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Text fx:id="lobbyIpText" fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Server Name" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1">
|
||||||
|
<font>
|
||||||
|
<Font size="29.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Text fx:id="timeUntilStart" fill="#f8f8f8" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
|
||||||
|
<font>
|
||||||
|
<Font size="20.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Text fx:id="courseNameText" fill="#e1e1e1" strokeType="OUTSIDE" strokeWidth="0.0" text="Course Name" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.chart.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.shape.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
<?import javafx.scene.chart.CategoryAxis?>
|
<?import javafx.scene.chart.CategoryAxis?>
|
||||||
<?import javafx.scene.chart.LineChart?>
|
<?import javafx.scene.chart.LineChart?>
|
||||||
<?import javafx.scene.chart.NumberAxis?>
|
<?import javafx.scene.chart.NumberAxis?>
|
||||||
@@ -17,40 +23,60 @@
|
|||||||
<?import javafx.scene.shape.Circle?>
|
<?import javafx.scene.shape.Circle?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
<?import javafx.scene.text.Text?>
|
<?import javafx.scene.text.Text?>
|
||||||
<GridPane prefHeight="960.0" prefWidth="1530.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
|
|
||||||
<columnConstraints>
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="998.0" prefWidth="1530.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
|
||||||
<ColumnConstraints maxWidth="246.0" minWidth="246.0" prefWidth="246.0" />
|
<children>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1034.0" />
|
<AnchorPane layoutX="322.0" layoutY="130.0" prefHeight="998.0" prefWidth="1281.0"
|
||||||
</columnConstraints>
|
style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0"
|
||||||
<rowConstraints>
|
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<children>
|
||||||
<RowConstraints />
|
<GridPane prefHeight="998.0" prefWidth="1281.0" AnchorPane.bottomAnchor="0.0"
|
||||||
<RowConstraints />
|
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
|
||||||
</rowConstraints>
|
AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<columnConstraints>
|
||||||
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3">
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="630.0" minWidth="10.0"
|
||||||
<children>
|
prefWidth="68.0"/>
|
||||||
<Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1213.0" minWidth="10.0"
|
||||||
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
|
prefWidth="1213.0"/>
|
||||||
<Label layoutX="11.0" layoutY="41.0" text="Timer" textFill="WHITE" />
|
</columnConstraints>
|
||||||
<Label layoutX="11.0" layoutY="112.0" text="Wind direction" textFill="WHITE" />
|
<rowConstraints>
|
||||||
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="190.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
<RowConstraints maxHeight="489.0" minHeight="1.0" prefHeight="24.0"
|
||||||
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="210.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
|
vgrow="SOMETIMES"/>
|
||||||
<font>
|
<RowConstraints maxHeight="997.0" minHeight="10.0" prefHeight="974.0"
|
||||||
<Font name="AdobeArabic-Regular" size="55.0" />
|
vgrow="SOMETIMES"/>
|
||||||
</font>
|
</rowConstraints>
|
||||||
</Text>
|
<children>
|
||||||
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="238.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
|
<AnchorPane fx:id="contentAnchorPane" prefHeight="200.0" prefWidth="200.0"
|
||||||
<font>
|
GridPane.columnSpan="2" GridPane.rowSpan="2"/>
|
||||||
<Font name="System Bold" size="13.0" />
|
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0" text="60 FPS"
|
||||||
</font>
|
GridPane.halignment="CENTER" GridPane.valignment="CENTER"/>
|
||||||
</Text>
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE" />
|
||||||
|
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
|
||||||
|
<Label layoutX="11.0" layoutY="41.0" text="Timer" textFill="WHITE" />
|
||||||
|
<Label layoutX="11.0" layoutY="112.0" text="Wind direction" textFill="WHITE" />
|
||||||
|
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="190.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
||||||
|
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="210.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
|
||||||
|
<font>
|
||||||
|
<Font name="AdobeArabic-Regular" size="55.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="238.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="13.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
<Text fx:id="windSpeedText" fill="#d3d3d3" layoutX="12.0" layoutY="237.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0 Knot" textAlignment="RIGHT">
|
<Text fx:id="windSpeedText" fill="#d3d3d3" layoutX="12.0" layoutY="237.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0 Knot" textAlignment="RIGHT">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="13.0" />
|
<Font name="System Bold" size="13.0" />
|
||||||
</font>
|
</font>
|
||||||
</Text>
|
</Text>
|
||||||
<CheckBox fx:id="toggleFps" focusTraversable="false" graphicTextGap="0.0" layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS" textFill="WHITE" />
|
<CheckBox fx:id="toggleFps" focusTraversable="false" graphicTextGap="0.0" layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS" textFill="WHITE" />
|
||||||
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="304.0" prefHeight="116.0" prefWidth="200.0" styleClass="text-white" />
|
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="304.0" prefHeight="116.0" prefWidth="200.0" styleClass="text-white" />
|
||||||
<Pane layoutX="11.0" layoutY="39.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
|
<Pane layoutX="11.0" layoutY="39.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
|
||||||
<children>
|
<children>
|
||||||
@@ -67,17 +93,14 @@
|
|||||||
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
||||||
<ComboBox fx:id="yachtSelectionComboBox" focusTraversable="false" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Yacht" styleClass="combo-box-base" />
|
<ComboBox fx:id="yachtSelectionComboBox" focusTraversable="false" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Yacht" styleClass="combo-box-base" />
|
||||||
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions">
|
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions">
|
||||||
<xAxis>
|
<xAxis>
|
||||||
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
|
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
|
||||||
</xAxis>
|
</xAxis>
|
||||||
<yAxis>
|
<yAxis>
|
||||||
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" />
|
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" />
|
||||||
</yAxis>
|
</yAxis>
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<AnchorPane fx:id="contentAnchorPane" style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
</AnchorPane>
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package seng302.gameServer.server;
|
|||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.gameServer.server.messages.BoatLocationMessage;
|
import seng302.gameServer.messages.BoatLocationMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test conversions used by the boat location messages
|
* Test conversions used by the boat location messages
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package seng302.gameServer.server;
|
|||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.gameServer.server.messages.Header;
|
import seng302.gameServer.messages.Header;
|
||||||
import seng302.gameServer.server.messages.MessageType;
|
import seng302.gameServer.messages.MessageType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests message header
|
* Tests message header
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import static junit.framework.TestCase.assertEquals;
|
|||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.messages.Message;
|
||||||
import seng302.gameServer.server.messages.XMLMessage;
|
import seng302.gameServer.messages.XMLMessage;
|
||||||
import seng302.gameServer.server.messages.XMLMessageSubType;
|
import seng302.gameServer.messages.XMLMessageSubType;
|
||||||
|
|
||||||
public class TestMessage {
|
public class TestMessage {
|
||||||
private static int XML_MESSAGE_LEN = 14;
|
private static int XML_MESSAGE_LEN = 14;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import org.junit.Test;
|
|||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
|
import static seng302.gameServer.GameState.checkCollision;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
|
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
|
||||||
* meters.
|
* meters.
|
||||||
@@ -37,7 +39,7 @@ public class UpdateYachtTest {
|
|||||||
if (!yacht1.getSailIn()) {
|
if (!yacht1.getSailIn()) {
|
||||||
yacht1.toggleSailIn();
|
yacht1.toggleSailIn();
|
||||||
}
|
}
|
||||||
GameState.checkForCollision(yacht1);
|
checkCollision(yacht1);
|
||||||
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
|
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
|
||||||
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
|
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
|
||||||
}
|
}
|
||||||
@@ -54,14 +56,14 @@ public class UpdateYachtTest {
|
|||||||
if (!yacht1.getSailIn()) {
|
if (!yacht1.getSailIn()) {
|
||||||
yacht1.toggleSailIn();
|
yacht1.toggleSailIn();
|
||||||
}
|
}
|
||||||
GameState.checkForCollision(yacht1);
|
checkCollision(yacht1);
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
|
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
|
||||||
)
|
)
|
||||||
); //Check that yachts are actually far enough apart for no collision.
|
); //Check that yachts are actually far enough apart for no collision.
|
||||||
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 0.001);
|
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 1.001);
|
||||||
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 0.001);
|
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 1.001);
|
||||||
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 0.001);
|
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 1.001);
|
||||||
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 0.001);
|
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 1.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ public class BoatSailAnimationToggleTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sailToggleTest() throws Exception {
|
public void sailToggleTest() throws Exception {
|
||||||
assertFalse(yacht.getSailIn());
|
|
||||||
yacht.toggleSail();
|
|
||||||
assertTrue(yacht.getSailIn());
|
assertTrue(yacht.getSailIn());
|
||||||
|
yacht.toggleSail();
|
||||||
|
assertFalse(yacht.getSailIn());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.junit.Assert;
|
|||||||
import seng302.gameServer.GameStages;
|
import seng302.gameServer.GameStages;
|
||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.gameServer.MainServerThread;
|
import seng302.gameServer.MainServerThread;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.messages.BoatAction;
|
||||||
import seng302.model.ServerYacht;
|
import seng302.model.ServerYacht;
|
||||||
import seng302.visualiser.ClientToServerThread;
|
import seng302.visualiser.ClientToServerThread;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user