Merge remote-tracking branch 'origin/raceView_UI' into nightmode
This commit is contained in:
@@ -66,6 +66,7 @@ public class Event {
|
|||||||
* Constructs an event, using various XML files.
|
* Constructs an event, using various XML files.
|
||||||
* @param singlePlayer Whether or not to create a single player event.
|
* @param singlePlayer Whether or not to create a single player event.
|
||||||
* @param mapIndex Specifies which map to use.
|
* @param mapIndex Specifies which map to use.
|
||||||
|
* @param raceLength The length of the race, in milliseconds.
|
||||||
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
|
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
|
||||||
*/
|
*/
|
||||||
public Event(boolean singlePlayer, int mapIndex, int raceLength) throws
|
public Event(boolean singlePlayer, int mapIndex, int raceLength) throws
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class MockBoat extends Boat {
|
|||||||
* 1: passed only first check
|
* 1: passed only first check
|
||||||
* 2: passed first and second check
|
* 2: passed first and second check
|
||||||
*/
|
*/
|
||||||
private Integer roundingStatus = 0;
|
private int roundingStatus = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores whether the boat is on autoVMG or not
|
* Stores whether the boat is on autoVMG or not
|
||||||
@@ -284,8 +284,8 @@ public class MockBoat extends Boat {
|
|||||||
(this.isStarboardSide(mark1) && this.isPortSide(mark2));
|
(this.isStarboardSide(mark1) && this.isPortSide(mark2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getRoundingStatus() {
|
public int getRoundingStatus() {
|
||||||
return Integer.valueOf(roundingStatus);
|
return roundingStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseRoundingStatus() {
|
public void increaseRoundingStatus() {
|
||||||
|
|||||||
@@ -419,6 +419,11 @@ public class MockRace extends RaceState {
|
|||||||
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
|
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove one unit of health for every frame spent outside boundary
|
||||||
|
if(!finish && !GPSCoordinate.isInsideBoundary(boat.getPosition(), getBoundary())) {
|
||||||
|
boat.updateHealth(-0.1);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateEstimatedTime(boat);
|
this.updateEstimatedTime(boat);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -430,7 +435,7 @@ public class MockRace extends RaceState {
|
|||||||
boat.getBearing()
|
boat.getBearing()
|
||||||
), boat.getBearing()) ;
|
), boat.getBearing()) ;
|
||||||
if (vmg.getSpeed() > 0) {
|
if (vmg.getSpeed() > 0) {
|
||||||
boat.setCurrentSpeed(vmg.getSpeed());
|
boat.setCurrentSpeed(vmg.getSpeed() * Math.pow(boat.getHealth() / 100, 0.3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +682,7 @@ public class MockRace extends RaceState {
|
|||||||
for (MockBoat boat : this.boats) {
|
for (MockBoat boat : this.boats) {
|
||||||
|
|
||||||
//If the boat is currently racing, count it.
|
//If the boat is currently racing, count it.
|
||||||
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
if (boat.getStatus() == BoatStatusEnum.RACING && boat.getHealth()>=1) {
|
||||||
numberOfActiveBoats++;
|
numberOfActiveBoats++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
|
|
||||||
//As long as there is at least one boat racing, we still simulate the race.
|
//As long as there is at least one boat racing, we still simulate the race.
|
||||||
if (race.getNumberOfActiveBoats() != 0) {
|
if (race.getNumberOfActiveBoats() != 0) {
|
||||||
|
//System.out.println(race.getNumberOfActiveBoats());
|
||||||
//Get the time period of this frame.
|
//Get the time period of this frame.
|
||||||
long framePeriod = currentTime - previousFrameTime;
|
long framePeriod = currentTime - previousFrameTime;
|
||||||
|
|
||||||
@@ -175,12 +175,9 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
||||||
|
|
||||||
if(race.getColliderRegistry().rayCast(boat)){
|
if(race.getColliderRegistry().rayCast(boat)){
|
||||||
//System.out.println("Collision!");
|
|
||||||
//Add boat to list
|
//Add boat to list
|
||||||
collisionBoats.add(boat);
|
collisionBoats.add(boat);
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println(race.getColliderRegistry().rayCast(boat));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public class RaceServer {
|
|||||||
//Parse the boat locations.
|
//Parse the boat locations.
|
||||||
snapshotMessages.addAll(parseBoatLocations());
|
snapshotMessages.addAll(parseBoatLocations());
|
||||||
|
|
||||||
|
//Parse the boat states
|
||||||
|
snapshotMessages.addAll(parseBoatStates());
|
||||||
|
|
||||||
//Parse the marks.
|
//Parse the marks.
|
||||||
snapshotMessages.addAll(parseMarks());
|
snapshotMessages.addAll(parseMarks());
|
||||||
|
|
||||||
@@ -243,7 +246,29 @@ public class RaceServer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates BoatState messages for every boat in the race
|
||||||
|
* @return list of BoatState messages
|
||||||
|
*/
|
||||||
|
private List<BoatState> parseBoatStates() {
|
||||||
|
List<BoatState> boatStates = new ArrayList<>();
|
||||||
|
for(MockBoat boat: race.getBoats()) {
|
||||||
|
boatStates.add(parseIndividualBoatState(boat));
|
||||||
|
}
|
||||||
|
return boatStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BoatState message for the current state of the given boat
|
||||||
|
* @param boat to generate message for
|
||||||
|
* @return BoatState message
|
||||||
|
*/
|
||||||
|
private BoatState parseIndividualBoatState(MockBoat boat) {
|
||||||
|
return new BoatState(
|
||||||
|
boat.getSourceID(),
|
||||||
|
(int)boat.getHealth()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the race status, and returns it.
|
* Parses the race status, and returns it.
|
||||||
|
|||||||
@@ -177,10 +177,10 @@ public class RaceXMLCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the destance between two marks
|
* gets the destance between two marks, in nautical miles.
|
||||||
* @param a mark 1
|
* @param a mark 1
|
||||||
* @param b mark 2
|
* @param b mark 2
|
||||||
* @return
|
* @return Distance in nautical miles.
|
||||||
*/
|
*/
|
||||||
private static double getDistance(XMLMark a, XMLMark b){
|
private static double getDistance(XMLMark a, XMLMark b){
|
||||||
GPSCoordinate coorda = new GPSCoordinate(a.getTargetLat(), a.getTargetLng());
|
GPSCoordinate coorda = new GPSCoordinate(a.getTargetLat(), a.getTargetLng());
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package network.MessageDecoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.bytesToInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoder for {@link BoatState} messages
|
||||||
|
*/
|
||||||
|
public class BoatStateDecoder implements MessageDecoder {
|
||||||
|
/**
|
||||||
|
* Decoded BoatState message
|
||||||
|
*/
|
||||||
|
private BoatState message;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||||
|
byte[] sourceID = Arrays.copyOfRange(encodedMessage, 0, 4);
|
||||||
|
byte boatHealth = encodedMessage[4];
|
||||||
|
|
||||||
|
// Unpack bytes into BoatState
|
||||||
|
this.message = new BoatState(
|
||||||
|
bytesToInt(sourceID),
|
||||||
|
boatHealth
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return BoatState
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoatState getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ public class DecoderFactory {
|
|||||||
|
|
||||||
case BOATACTION: return new BoatActionDecoder();
|
case BOATACTION: return new BoatActionDecoder();
|
||||||
|
|
||||||
|
case BOATSTATE: return new BoatStateDecoder();
|
||||||
|
|
||||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@ public interface MessageDecoder {
|
|||||||
* @return The decoded message.
|
* @return The decoded message.
|
||||||
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
||||||
*/
|
*/
|
||||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException;
|
AC35Data decode(byte[] encodedMessage) throws InvalidMessageException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package network.MessageEncoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.intToBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoder for {@link BoatState} message
|
||||||
|
*/
|
||||||
|
public class BoatStateEncoder implements MessageEncoder {
|
||||||
|
@Override
|
||||||
|
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||||
|
// Downcast message
|
||||||
|
BoatState boatState = (BoatState)message;
|
||||||
|
|
||||||
|
//Serialise message
|
||||||
|
byte[] sourceID = intToBytes(boatState.getSourceID());
|
||||||
|
byte boatHealth = (byte)boatState.getBoatHealth();
|
||||||
|
|
||||||
|
// Pack bytes into string
|
||||||
|
ByteBuffer boatStateMessage = ByteBuffer.allocate(5);
|
||||||
|
boatStateMessage.put(sourceID);
|
||||||
|
boatStateMessage.put(boatHealth);
|
||||||
|
|
||||||
|
// Return byte string
|
||||||
|
return boatStateMessage.array();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ public class EncoderFactory {
|
|||||||
|
|
||||||
case BOATACTION: return new BoatActionEncoder();
|
case BOATACTION: return new BoatActionEncoder();
|
||||||
|
|
||||||
|
case BOATSTATE: return new BoatStateEncoder();
|
||||||
|
|
||||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package network.Messages;
|
||||||
|
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the information in a BoatState message according to protocol meeting
|
||||||
|
*/
|
||||||
|
public class BoatState extends AC35Data {
|
||||||
|
/**
|
||||||
|
* Source ID of boat described in message
|
||||||
|
*/
|
||||||
|
private int sourceID;
|
||||||
|
/**
|
||||||
|
* Health between 0-100 of boat with above source ID
|
||||||
|
*/
|
||||||
|
private int boatHealth;
|
||||||
|
|
||||||
|
public BoatState(int sourceID, int boatHealth) {
|
||||||
|
super(MessageType.BOATSTATE);
|
||||||
|
this.sourceID = sourceID;
|
||||||
|
this.boatHealth = boatHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSourceID() {
|
||||||
|
return sourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBoatHealth() {
|
||||||
|
return boatHealth;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,8 @@ public enum MessageType {
|
|||||||
|
|
||||||
HOSTED_GAMES_REQUEST(109),
|
HOSTED_GAMES_REQUEST(109),
|
||||||
|
|
||||||
|
BOATSTATE(103),
|
||||||
|
|
||||||
NOTAMESSAGE(0);
|
NOTAMESSAGE(0);
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ public enum MessageType {
|
|||||||
* Creates a MessageType enum from a given primitive integer value, cast to a byte.
|
* Creates a MessageType enum from a given primitive integer value, cast to a byte.
|
||||||
* @param value Integer, which is cast to byte, to construct from.
|
* @param value Integer, which is cast to byte, to construct from.
|
||||||
*/
|
*/
|
||||||
private MessageType(int value) {
|
MessageType(int value) {
|
||||||
this.value = (byte)value;
|
this.value = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ public class Boat extends Collider {
|
|||||||
*/
|
*/
|
||||||
private boolean isColliding = false;
|
private boolean isColliding = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of health boat currently has, between 0 and 100
|
||||||
|
*/
|
||||||
|
private double health;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table.
|
* Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table.
|
||||||
*
|
*
|
||||||
@@ -113,6 +118,7 @@ public class Boat extends Collider {
|
|||||||
public Boat(int sourceID, String name, String country) {
|
public Boat(int sourceID, String name, String country) {
|
||||||
|
|
||||||
this.sourceID = sourceID;
|
this.sourceID = sourceID;
|
||||||
|
this.health = 100;
|
||||||
this.setName(name);
|
this.setName(name);
|
||||||
this.setCountry(country);
|
this.setCountry(country);
|
||||||
|
|
||||||
@@ -419,6 +425,8 @@ public class Boat extends Collider {
|
|||||||
@Override
|
@Override
|
||||||
public void onCollisionEnter(Collision e) {
|
public void onCollisionEnter(Collision e) {
|
||||||
if(e.getBearing().degrees() > 270 || e.getBearing().degrees() < 90) {
|
if(e.getBearing().degrees() > 270 || e.getBearing().degrees() < 90) {
|
||||||
|
// Deplete health
|
||||||
|
e.getBoat().updateHealth(-5);
|
||||||
// Notify observers of collision
|
// Notify observers of collision
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
notifyObservers(e);
|
notifyObservers(e);
|
||||||
@@ -432,4 +440,22 @@ public class Boat extends Collider {
|
|||||||
public void setColliding(boolean colliding) {
|
public void setColliding(boolean colliding) {
|
||||||
isColliding = colliding;
|
isColliding = colliding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getHealth() {
|
||||||
|
return health;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealth(double health) {
|
||||||
|
this.health = health;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a given amount of HP to boat health
|
||||||
|
* @param delta amount of HP to add
|
||||||
|
*/
|
||||||
|
public void updateHealth(double delta) {
|
||||||
|
health += delta;
|
||||||
|
if(health < 0) health = 0;
|
||||||
|
else if(health > 100) health = 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public class Mark extends Collider{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCollisionEnter(Collision e) {
|
public void onCollisionEnter(Collision e) {
|
||||||
|
e.getBoat().updateHealth(-10);
|
||||||
this.setChanged();
|
this.setChanged();
|
||||||
notifyObservers(e);
|
notifyObservers(e);
|
||||||
}
|
}
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package visualiser.Commands.VisualiserRaceCommands;
|
||||||
|
|
||||||
|
import mock.model.commandFactory.Command;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
import shared.exceptions.BoatNotFoundException;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
import visualiser.model.VisualiserRaceState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates boats on visualiser when their health changes
|
||||||
|
*/
|
||||||
|
public class BoatStateCommand implements Command {
|
||||||
|
private BoatState boatState;
|
||||||
|
private VisualiserRaceState visualiserRace;
|
||||||
|
|
||||||
|
public BoatStateCommand(BoatState boatState, VisualiserRaceState visualiserRace) {
|
||||||
|
this.boatState = boatState;
|
||||||
|
this.visualiserRace = visualiserRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
try {
|
||||||
|
VisualiserBoat boat = visualiserRace.getBoat(boatState.getSourceID());
|
||||||
|
boat.setHealth(boatState.getBoatHealth());
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
// Fail silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -24,7 +24,6 @@ public class VisualiserRaceCommandFactory {
|
|||||||
switch (message.getType()) {
|
switch (message.getType()) {
|
||||||
|
|
||||||
case BOATLOCATION:
|
case BOATLOCATION:
|
||||||
//System.out.println("Boat location received");
|
|
||||||
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
|
||||||
|
|
||||||
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
|
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
|
||||||
@@ -36,7 +35,8 @@ public class VisualiserRaceCommandFactory {
|
|||||||
case YACHTEVENTCODE:
|
case YACHTEVENTCODE:
|
||||||
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
|
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
|
||||||
|
|
||||||
|
case BOATSTATE:
|
||||||
|
return new BoatStateCommand((BoatState) message, visualiserRace);
|
||||||
|
|
||||||
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
|
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ import javafx.scene.AmbientLight;
|
|||||||
import javafx.scene.PointLight;
|
import javafx.scene.PointLight;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.media.AudioClip;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Material;
|
import javafx.scene.paint.Material;
|
||||||
import javafx.scene.paint.PhongMaterial;
|
import javafx.scene.paint.PhongMaterial;
|
||||||
@@ -28,6 +31,7 @@ import javafx.scene.shape.MeshView;
|
|||||||
import javafx.scene.shape.Shape3D;
|
import javafx.scene.shape.Shape3D;
|
||||||
import javafx.scene.transform.Translate;
|
import javafx.scene.transform.Translate;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
import shared.dataInput.RaceDataSource;
|
import shared.dataInput.RaceDataSource;
|
||||||
import shared.exceptions.BoatNotFoundException;
|
import shared.exceptions.BoatNotFoundException;
|
||||||
@@ -42,6 +46,7 @@ import visualiser.model.*;
|
|||||||
import visualiser.utils.GPSConverter;
|
import visualiser.utils.GPSConverter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@@ -70,6 +75,9 @@ public class RaceViewController extends Controller {
|
|||||||
private long positionTime = 0;
|
private long positionTime = 0;
|
||||||
private ResizableRaceCanvas raceCanvas;
|
private ResizableRaceCanvas raceCanvas;
|
||||||
private boolean mapToggle = true;
|
private boolean mapToggle = true;
|
||||||
|
private GPSConverter gpsConverter;
|
||||||
|
private ArrayList<HealthEffect> healthEffectList = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrow pointing to next mark in third person
|
* Arrow pointing to next mark in third person
|
||||||
@@ -98,12 +106,17 @@ public class RaceViewController extends Controller {
|
|||||||
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
||||||
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
||||||
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, Number> boatHealthColumn;
|
||||||
private @FXML LineChart<Number, Number> sparklineChart;
|
private @FXML LineChart<Number, Number> sparklineChart;
|
||||||
private @FXML Label tutorialText;
|
private @FXML Label tutorialText;
|
||||||
private @FXML AnchorPane infoWrapper;
|
private @FXML AnchorPane infoWrapper;
|
||||||
private @FXML AnchorPane lineChartWrapper;
|
private @FXML AnchorPane lineChartWrapper;
|
||||||
private @FXML StackPane speedPane;
|
private @FXML StackPane speedPane;
|
||||||
private @FXML AnchorPane raceAnchorPane;
|
private @FXML AnchorPane raceAnchorPane;
|
||||||
|
private @FXML GridPane playerHealthContainer;
|
||||||
|
private @FXML ImageView imageView;
|
||||||
|
private @FXML AnchorPane deathTransPane;
|
||||||
|
private @FXML StackPane deathPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a specified race.
|
* Displays a specified race.
|
||||||
@@ -118,10 +131,13 @@ public class RaceViewController extends Controller {
|
|||||||
this.controllerClient = controllerClient;
|
this.controllerClient = controllerClient;
|
||||||
this.isHost = isHost;
|
this.isHost = isHost;
|
||||||
keyFactory.load();
|
keyFactory.load();
|
||||||
|
deathPane.setDisable(false);
|
||||||
|
deathPane.setVisible(false);
|
||||||
tutorialCheck();
|
tutorialCheck();
|
||||||
initKeypressHandler();
|
initKeypressHandler();
|
||||||
|
healthLoop();
|
||||||
initialiseRaceVisuals();
|
initialiseRaceVisuals();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +149,6 @@ public class RaceViewController extends Controller {
|
|||||||
isTutorial = true;
|
isTutorial = true;
|
||||||
tutorialText.setVisible(true);
|
tutorialText.setVisible(true);
|
||||||
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
||||||
|
|
||||||
currentState = tutorialStates.get(0);
|
currentState = tutorialStates.get(0);
|
||||||
tutorialStates.remove(0);
|
tutorialStates.remove(0);
|
||||||
searchMapForKey("Upwind");
|
searchMapForKey("Upwind");
|
||||||
@@ -294,6 +309,7 @@ public class RaceViewController extends Controller {
|
|||||||
initialiseFps();
|
initialiseFps();
|
||||||
initialiseInfoTable();
|
initialiseInfoTable();
|
||||||
initialiseView3D(this.visualiserRace);
|
initialiseView3D(this.visualiserRace);
|
||||||
|
initialiseHealthPane();
|
||||||
initialiseRaceClock();
|
initialiseRaceClock();
|
||||||
initialiseSpeedometer();
|
initialiseSpeedometer();
|
||||||
initialiseRaceCanvas();
|
initialiseRaceCanvas();
|
||||||
@@ -301,8 +317,23 @@ public class RaceViewController extends Controller {
|
|||||||
//nextMarkPane.toFront();
|
//nextMarkPane.toFront();
|
||||||
speedometerLoop();
|
speedometerLoop();
|
||||||
new Sparkline(this.raceState, this.sparklineChart);
|
new Sparkline(this.raceState, this.sparklineChart);
|
||||||
timeZone.setText(this.raceState.getRaceClock().getTimeZone());
|
}
|
||||||
arrowController.setWindProperty(this.raceState.windProperty());
|
|
||||||
|
|
||||||
|
|
||||||
|
private void initialiseHealthPane() {
|
||||||
|
InputStream tomato = this.getClass().getClassLoader().getResourceAsStream("visualiser/images/tomato.png");
|
||||||
|
HealthSlider healthSlider = new HealthSlider(new Image(tomato));
|
||||||
|
playerHealthContainer.add(healthSlider, 0, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
VisualiserBoat player = raceState.getBoat(raceState.getPlayerBoatID());
|
||||||
|
player.healthProperty().addListener((o, prev, curr) -> {
|
||||||
|
healthSlider.setCrop((double)curr/100.0);
|
||||||
|
});
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseView3D(VisualiserRaceEvent race) {
|
private void initialiseView3D(VisualiserRaceEvent race) {
|
||||||
@@ -357,7 +388,7 @@ public class RaceViewController extends Controller {
|
|||||||
|
|
||||||
// Set up projection from GPS to view
|
// Set up projection from GPS to view
|
||||||
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
||||||
final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450);
|
gpsConverter = new GPSConverter(raceData, 450, 450);
|
||||||
|
|
||||||
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
|
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
|
||||||
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
|
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
|
||||||
@@ -438,6 +469,11 @@ public class RaceViewController extends Controller {
|
|||||||
viewSubjects.add(boatModel);
|
viewSubjects.add(boatModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create health effect
|
||||||
|
HealthEffect healthEffect = new HealthEffect(boat.getSourceID(), System.currentTimeMillis());
|
||||||
|
viewSubjects.add(healthEffect);
|
||||||
|
healthEffectList.add(healthEffect);
|
||||||
|
|
||||||
//add sail
|
//add sail
|
||||||
Sails3D sails3D = new Sails3D();
|
Sails3D sails3D = new Sails3D();
|
||||||
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
||||||
@@ -518,6 +554,14 @@ public class RaceViewController extends Controller {
|
|||||||
boatModel.setHeading(boat.getBearing().degrees());
|
boatModel.setHeading(boat.getBearing().degrees());
|
||||||
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
boatModel.getMesh().toFront();
|
||||||
|
|
||||||
|
//Fire follows boat
|
||||||
|
healthEffect.setHeading(boat.getBearing().degrees());
|
||||||
|
healthEffect.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
||||||
|
healthEffect.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
||||||
|
healthEffect.setY(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
trackBoat.start();
|
trackBoat.start();
|
||||||
@@ -710,13 +754,17 @@ public class RaceViewController extends Controller {
|
|||||||
// set table data
|
// set table data
|
||||||
boatInfoTable.setItems(sortedBoats);
|
boatInfoTable.setItems(sortedBoats);
|
||||||
boatTeamColumn.setCellValueFactory(
|
boatTeamColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().nameProperty());
|
cellData -> cellData.getValue().nameProperty()
|
||||||
|
);
|
||||||
boatSpeedColumn.setCellValueFactory(
|
boatSpeedColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().currentSpeedProperty());
|
cellData -> cellData.getValue().currentSpeedProperty()
|
||||||
|
);
|
||||||
boatMarkColumn.setCellValueFactory(
|
boatMarkColumn.setCellValueFactory(
|
||||||
cellData -> cellData.getValue().legProperty());
|
cellData -> cellData.getValue().legProperty()
|
||||||
boatPlacingColumn.setCellValueFactory(
|
);
|
||||||
cellData -> cellData.getValue().placingProperty());
|
boatHealthColumn.setCellValueFactory(
|
||||||
|
cellData -> cellData.getValue().healthProperty()
|
||||||
|
);
|
||||||
|
|
||||||
//Kind of ugly, but allows for formatting an observed speed.
|
//Kind of ugly, but allows for formatting an observed speed.
|
||||||
boatSpeedColumn.setCellFactory(
|
boatSpeedColumn.setCellFactory(
|
||||||
@@ -848,6 +896,64 @@ public class RaceViewController extends Controller {
|
|||||||
}.start();
|
}.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation timer for health
|
||||||
|
*/
|
||||||
|
private void healthLoop(){
|
||||||
|
new AnimationTimer(){
|
||||||
|
@Override
|
||||||
|
public void handle(long arg0){
|
||||||
|
if (raceState.getRaceStatusEnum() == RaceStatusEnum.FINISHED) {
|
||||||
|
stop(); // stop the timer
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
//Check if boat is dead
|
||||||
|
if(raceState.getBoat(raceState.getPlayerBoatID()).getHealth()<=0){
|
||||||
|
if(!deathPane.isDisable()) {
|
||||||
|
deathPane.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(VisualiserBoat boat : raceState.getBoats()){
|
||||||
|
for (HealthEffect fp : healthEffectList){
|
||||||
|
|
||||||
|
if(fp.getSourceID()==boat.getSourceID()){
|
||||||
|
if(boat.getHealth()<=0){
|
||||||
|
//Boat is dead. Don't check it anymore for hp
|
||||||
|
fp.displayDeath(fp.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
fp.setSourceID(0);
|
||||||
|
try {
|
||||||
|
raceState.getBoat(boat.getSourceID()).setStatus(BoatStatusEnum.DNF);
|
||||||
|
|
||||||
|
raceState.updateBoatPositions(raceState.getBoats());
|
||||||
|
} catch (BoatNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//Speed up tick when <=10 hp
|
||||||
|
if(boat.getHealth()<=10){
|
||||||
|
fp.flash(System.currentTimeMillis(), 300, boat.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
//Visual indication of low hp
|
||||||
|
if(boat.getHealth()<=20) {
|
||||||
|
//fp.setVisible(true);
|
||||||
|
fp.flash(System.currentTimeMillis(), 500, boat.getSourceID()==raceState.getPlayerBoatID());
|
||||||
|
} else {
|
||||||
|
fp.setVisible(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggles if the info table is shown
|
* toggles if the info table is shown
|
||||||
@@ -1004,4 +1110,12 @@ public class RaceViewController extends Controller {
|
|||||||
mapToggle = !mapToggle;
|
mapToggle = !mapToggle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FXML method for death button
|
||||||
|
*/
|
||||||
|
public void deathOKPressed(){
|
||||||
|
deathPane.setDisable(true);
|
||||||
|
deathPane.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.geometry.Point3D;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.media.AudioClip;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.*;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by zwu18 on 24/09/17.
|
||||||
|
*/
|
||||||
|
public class HealthEffect extends Subject3D {
|
||||||
|
|
||||||
|
private int sourceID;
|
||||||
|
private long currentTime;
|
||||||
|
private AudioClip warningSound = new AudioClip(this.getClass().getResource("/visualiser/sounds/warning.mp3").toExternalForm());
|
||||||
|
private AudioClip deadSound = new AudioClip(this.getClass().getResource("/visualiser/sounds/dead1.wav").toExternalForm());
|
||||||
|
|
||||||
|
public HealthEffect(int sourceID, long currentTime){
|
||||||
|
super(createEffect(), 0);
|
||||||
|
this.sourceID = sourceID;
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the mesh view with image
|
||||||
|
* @return Mesh view
|
||||||
|
*/
|
||||||
|
private static Shape3D createEffect(){
|
||||||
|
|
||||||
|
Image image = new Image(HealthEffect.class.getClassLoader().getResourceAsStream("images/warning.png"));
|
||||||
|
|
||||||
|
Plane3D plane = new Plane3D(20, 20, 10, 10);
|
||||||
|
|
||||||
|
PhongMaterial material = new PhongMaterial();
|
||||||
|
material.setDiffuseColor(Color.web("#FFFFFF"));
|
||||||
|
material.setSpecularColor(Color.web("#000000"));
|
||||||
|
material.setDiffuseMap(image);
|
||||||
|
|
||||||
|
MeshView imageSurface = new MeshView(plane);
|
||||||
|
|
||||||
|
|
||||||
|
imageSurface.setMaterial(material);
|
||||||
|
imageSurface.setMouseTransparent(true);
|
||||||
|
//imageSurface.toFront(); this.flashInterval = flashInterval;
|
||||||
|
|
||||||
|
return imageSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotateView(Double angle, Double pivotX, Double pivotY, Double pivotZ, Point3D axis){
|
||||||
|
Rotate rotate = new Rotate(angle, axis);
|
||||||
|
rotate.setPivotX(pivotX);
|
||||||
|
rotate.setPivotY(pivotY);
|
||||||
|
rotate.setPivotZ(pivotZ);
|
||||||
|
this.getMesh().getTransforms().add(rotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisible(boolean bool){
|
||||||
|
this.getMesh().setVisible(bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getSourceID(){
|
||||||
|
return sourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceID(int id){
|
||||||
|
this.sourceID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display visual indication when boat dies
|
||||||
|
* @param player boolean if player is current user or not
|
||||||
|
*/
|
||||||
|
public void displayDeath(boolean player){
|
||||||
|
Image image = new Image(HealthEffect.class.getClassLoader().getResourceAsStream("images/warning2.png"));
|
||||||
|
PhongMaterial material = (PhongMaterial) this.getMesh().getMaterial();
|
||||||
|
material.setDiffuseColor(Color.web("#FFFFFF"));
|
||||||
|
material.setSpecularColor(Color.web("#000000"));
|
||||||
|
material.setDiffuseMap(image);
|
||||||
|
this.getMesh().setMaterial(material);
|
||||||
|
if(player) {
|
||||||
|
deadSound.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash the mesh view at a certain interval
|
||||||
|
* @param checkTime The current time of flash
|
||||||
|
* @param flashInterval Desired flash interval
|
||||||
|
* @param playerBoat Whether or not this effect is for the player's boat.
|
||||||
|
*/
|
||||||
|
public void flash(long checkTime, long flashInterval, boolean playerBoat){
|
||||||
|
if(checkTime >= (currentTime+flashInterval)){
|
||||||
|
this.currentTime = checkTime;
|
||||||
|
if(this.getMesh().isVisible()){
|
||||||
|
this.setVisible(false);
|
||||||
|
} else {
|
||||||
|
if(playerBoat) {
|
||||||
|
warningSound.setVolume(0.1);
|
||||||
|
warningSound.play();
|
||||||
|
}
|
||||||
|
this.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by connortaylorbrown on 21/09/17.
|
||||||
|
*/
|
||||||
|
public class HealthSlider extends Pane {
|
||||||
|
/**
|
||||||
|
* Image used to fill health slider
|
||||||
|
*/
|
||||||
|
private Image fillImage;
|
||||||
|
/**
|
||||||
|
* Size of background for image configuration
|
||||||
|
*/
|
||||||
|
private BackgroundSize backgroundSize;
|
||||||
|
/**
|
||||||
|
* Percentage of image cropped out from top
|
||||||
|
*/
|
||||||
|
private double crop;
|
||||||
|
|
||||||
|
public HealthSlider(Image fillImage) {
|
||||||
|
this.fillImage = fillImage;
|
||||||
|
this.crop = 1;
|
||||||
|
this.backgroundSize = new BackgroundSize(
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
drawSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCrop(double crop) {
|
||||||
|
this.crop = crop;
|
||||||
|
drawSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawSlider() {
|
||||||
|
int top = Math.max(0,(int)(fillImage.getHeight() - crop * fillImage.getHeight()));
|
||||||
|
|
||||||
|
WritableImage croppedImage = new WritableImage(
|
||||||
|
fillImage.getPixelReader(),
|
||||||
|
0,
|
||||||
|
top,
|
||||||
|
(int)fillImage.getWidth(),
|
||||||
|
(int)fillImage.getHeight() - top
|
||||||
|
);
|
||||||
|
|
||||||
|
BackgroundImage backgroundImage = new BackgroundImage(
|
||||||
|
croppedImage,
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundPosition.CENTER,
|
||||||
|
backgroundSize
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setBackground(new Background(backgroundImage));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package visualiser.model;
|
package visualiser.model;
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import network.Messages.Enums.BoatStatusEnum;
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import shared.model.*;
|
import shared.model.*;
|
||||||
@@ -65,6 +62,7 @@ public class VisualiserBoat extends Boat {
|
|||||||
private ObjectProperty<GPSCoordinate> positionProperty;
|
private ObjectProperty<GPSCoordinate> positionProperty;
|
||||||
private ObjectProperty<Bearing> bearingProperty;
|
private ObjectProperty<Bearing> bearingProperty;
|
||||||
private BooleanProperty hasCollided;
|
private BooleanProperty hasCollided;
|
||||||
|
private DoubleProperty healthProperty;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +76,7 @@ public class VisualiserBoat extends Boat {
|
|||||||
|
|
||||||
this.color = color;
|
this.color = color;
|
||||||
this.hasCollided = new SimpleBooleanProperty(false);
|
this.hasCollided = new SimpleBooleanProperty(false);
|
||||||
|
this.healthProperty = new SimpleDoubleProperty(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -284,4 +283,18 @@ public class VisualiserBoat extends Boat {
|
|||||||
public void setHasCollided(boolean hasCollided) {
|
public void setHasCollided(boolean hasCollided) {
|
||||||
this.hasCollided.set(hasCollided);
|
this.hasCollided.set(hasCollided);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DoubleProperty healthProperty() {
|
||||||
|
return healthProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getHealth() {
|
||||||
|
return healthProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHealth(double healthProperty) {
|
||||||
|
this.healthProperty.set((int)healthProperty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,6 @@ public class VisualiserRaceState extends RaceState {
|
|||||||
|
|
||||||
|
|
||||||
if ((boat.getStatus() == BoatStatusEnum.DNF) || (boat.getStatus() == BoatStatusEnum.PRESTART) || (boat.getCurrentLeg().getLegNumber() < 0)) {
|
if ((boat.getStatus() == BoatStatusEnum.DNF) || (boat.getStatus() == BoatStatusEnum.PRESTART) || (boat.getCurrentLeg().getLegNumber() < 0)) {
|
||||||
|
|
||||||
boat.setPlacing("-");
|
boat.setPlacing("-");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ public class ServerConnection implements RunnableWithFramePeriod {
|
|||||||
this.messageRouter.addRoute(MessageType.MARKROUNDING, incomingMessages);
|
this.messageRouter.addRoute(MessageType.MARKROUNDING, incomingMessages);
|
||||||
this.messageRouter.addRoute(MessageType.XMLMESSAGE, incomingMessages);
|
this.messageRouter.addRoute(MessageType.XMLMESSAGE, incomingMessages);
|
||||||
this.messageRouter.addRoute(MessageType.ASSIGN_PLAYER_BOAT, incomingMessages);
|
this.messageRouter.addRoute(MessageType.ASSIGN_PLAYER_BOAT, incomingMessages);
|
||||||
|
this.messageRouter.addRoute(MessageType.BOATSTATE, incomingMessages);
|
||||||
this.messageRouter.removeDefaultRoute(); //We no longer want to keep un-routed messages.
|
this.messageRouter.removeDefaultRoute(); //We no longer want to keep un-routed messages.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
@@ -98,19 +98,22 @@
|
|||||||
<Image url="@../../images/raceViewUI_LowerLeft.png" />
|
<Image url="@../../images/raceViewUI_LowerLeft.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
<Label fx:id="FPS" mouseTransparent="true" text="FPS: 0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<Label fx:id="FPS" mouseTransparent="true" text="FPS: 0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="15.0" />
|
<Font name="System Bold" size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
|
</Label>
|
||||||
|
<GridPane fx:id="playerHealthContainer" prefHeight="75.0" prefWidth="75.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets top="20.0" />
|
<Insets left="10.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</Label>
|
</GridPane>
|
||||||
<Label fx:id="timeZone" text="Label" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<font>
|
|
||||||
<Font name="System Bold" size="15.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label fx:id="tutorialText" alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" style="-fx-border-color: orange; -fx-border-radius: 5px; -fx-background-color: #ffffcc; -fx-text-fill: #3399ff; -fx-border-width: 3; -fx-border-insets: -3;" text="This is the tutorial text" visible="false" AnchorPane.leftAnchor="150.0" AnchorPane.rightAnchor="150.0" AnchorPane.topAnchor="150.0" />
|
<Label fx:id="tutorialText" alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" style="-fx-border-color: orange; -fx-border-radius: 5px; -fx-background-color: #ffffcc; -fx-text-fill: #3399ff; -fx-border-width: 3; -fx-border-insets: -3;" text="This is the tutorial text" visible="false" AnchorPane.leftAnchor="150.0" AnchorPane.rightAnchor="150.0" AnchorPane.topAnchor="150.0" />
|
||||||
<StackPane fx:id="speedPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="2.0">
|
<StackPane fx:id="speedPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="2.0">
|
||||||
<padding>
|
<padding>
|
||||||
@@ -175,6 +178,30 @@
|
|||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
<ImageView fx:id="imageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
<StackPane fx:id="deathPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<AnchorPane mouseTransparent="true" opacity="0.31" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: grey;" />
|
||||||
|
<Label alignment="CENTER" text="You have died..." StackPane.alignment="TOP_CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="34.0" />
|
||||||
|
</font>
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets top="100.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="CENTER" text="But you can still watch the race!" StackPane.alignment="TOP_CENTER">
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets top="165.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Button alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#deathOKPressed" prefHeight="25.0" prefWidth="52.0" text="OK" StackPane.alignment="TOP_CENTER">
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets top="200.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<ImageView fitHeight="150.0" fitWidth="200.0" focusTraversable="true" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" StackPane.alignment="BOTTOM_CENTER">
|
<ImageView fitHeight="150.0" fitWidth="200.0" focusTraversable="true" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" StackPane.alignment="BOTTOM_CENTER">
|
||||||
@@ -235,10 +262,10 @@
|
|||||||
<children>
|
<children>
|
||||||
<TableView fx:id="boatInfoTable" minWidth="475.0" prefHeight="214.0" prefWidth="475.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
<TableView fx:id="boatInfoTable" minWidth="475.0" prefHeight="214.0" prefWidth="475.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn fx:id="boatPlacingColumn" prefWidth="50.0" text="Place" />
|
<TableColumn fx:id="boatTeamColumn" prefWidth="135.0" text="Team" />
|
||||||
<TableColumn fx:id="boatTeamColumn" prefWidth="200.0" text="Team" />
|
<TableColumn fx:id="boatMarkColumn" prefWidth="142.0" text="Mark" />
|
||||||
<TableColumn fx:id="boatMarkColumn" prefWidth="150.0" text="Mark" />
|
<TableColumn fx:id="boatHealthColumn" prefWidth="110.0" text="Health" />
|
||||||
<TableColumn fx:id="boatSpeedColumn" prefWidth="75.0" text="Speed" />
|
<TableColumn fx:id="boatSpeedColumn" prefWidth="87.0" text="Speed" />
|
||||||
</columns>
|
</columns>
|
||||||
</TableView>
|
</TableView>
|
||||||
<AnchorPane fx:id="lineChartWrapper" prefHeight="167.0" prefWidth="178.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
<AnchorPane fx:id="lineChartWrapper" prefHeight="167.0" prefWidth="178.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
|||||||
|
package network.MessageDecoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||||
|
import network.Messages.BoatState;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for the {@link network.Messages.BoatState} encoder and decoder.
|
||||||
|
*/
|
||||||
|
public class BoatStateDecoderTest {
|
||||||
|
private BoatState originalMessage;
|
||||||
|
private BoatState decodedMessage;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws InvalidMessageException {
|
||||||
|
originalMessage = new BoatState(0, 100);
|
||||||
|
|
||||||
|
byte[] encodedMessage = RaceVisionByteEncoder.encode(originalMessage);
|
||||||
|
BoatStateDecoder decoder = new BoatStateDecoder();
|
||||||
|
decoder.decode(encodedMessage);
|
||||||
|
decodedMessage = decoder.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decodingEqualsOriginal() {
|
||||||
|
assertEquals(originalMessage.getSourceID(), decodedMessage.getSourceID());
|
||||||
|
assertEquals(originalMessage.getBoatHealth(), decodedMessage.getBoatHealth());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user