Merge branch 'story1275_host_customization' into 1273_Skybox
# Conflicts: # src/main/java/seng302/visualiser/GameView3D.java # src/main/java/seng302/visualiser/controllers/LobbyController.java # src/main/java/seng302/visualiser/controllers/ViewManager.java # src/main/resources/views/LobbyView.fxml # src/main/resources/views/RaceView.fxml
@@ -1,23 +1,39 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import seng302.gameServer.messages.*;
|
||||
import seng302.model.*;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatStatus;
|
||||
import seng302.gameServer.messages.ChatterMessage;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.MarkRoundingMessage;
|
||||
import seng302.gameServer.messages.MarkType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RoundingBoatStatus;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.gameServer.messages.YachtEventType;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.PolarTable;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.mark.MarkOrder;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.util.*;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
/**
|
||||
@@ -58,7 +74,6 @@ public class GameState implements Runnable {
|
||||
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
|
||||
private static Boolean playerHasLeftFlag;
|
||||
|
||||
private static String hostIpAddress;
|
||||
private static List<Player> players;
|
||||
private static Map<Integer, ServerYacht> yachts;
|
||||
private static Boolean isRaceStarted;
|
||||
@@ -77,14 +92,13 @@ public class GameState implements Runnable {
|
||||
|
||||
private static Map<Player, String> playerStringMap = new HashMap<>();
|
||||
|
||||
public GameState(String hostIpAddress) {
|
||||
public GameState() {
|
||||
windDirection = 180d;
|
||||
windSpeed = 10000d;
|
||||
yachts = new HashMap<>();
|
||||
tokensInPlay = new ArrayList<>();
|
||||
|
||||
marks = new HashSet<>();
|
||||
players = new ArrayList<>();
|
||||
GameState.hostIpAddress = hostIpAddress;
|
||||
customizationFlag = false;
|
||||
playerHasLeftFlag = false;
|
||||
speedMultiplier = 1.0;
|
||||
@@ -92,34 +106,20 @@ public class GameState implements Runnable {
|
||||
isRaceStarted = false;
|
||||
//set this when game stage changes to prerace
|
||||
previousUpdateTime = System.currentTimeMillis();
|
||||
markOrder = new MarkOrder(); //This could be instantiated at some point with a select map?
|
||||
newMessageListeners = new ArrayList<>();
|
||||
allTokens = makeTokens();
|
||||
|
||||
resetStartTime();
|
||||
|
||||
new Thread(this, "GameState").start(); //Run the auto updates on the game state
|
||||
|
||||
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);
|
||||
public static void setRace(RaceXMLData raceXMLData) {
|
||||
markOrder = new MarkOrder(raceXMLData);
|
||||
for (CompoundMark compoundMark : raceXMLData.getCompoundMarks().values()){
|
||||
marks.addAll(compoundMark.getMarks());
|
||||
}
|
||||
courseLimit = XMLParser.parseRace(document).getCourseLimit();
|
||||
courseLimit = raceXMLData.getCourseLimit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a pre defined set of tokensInPlay. //TODO wmu16 - Should read from some file for each
|
||||
* race ideally
|
||||
@@ -134,10 +134,6 @@ public class GameState implements Runnable {
|
||||
return new ArrayList<>(Arrays.asList(token1, token2, token3, token4));
|
||||
}
|
||||
|
||||
public static String getHostIpAddress() {
|
||||
return hostIpAddress;
|
||||
}
|
||||
|
||||
public static Set<Mark> getMarks() {
|
||||
return Collections.unmodifiableSet(marks);
|
||||
}
|
||||
@@ -245,10 +241,6 @@ public class GameState implements Runnable {
|
||||
if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) {
|
||||
update();
|
||||
}
|
||||
|
||||
if (currentStage == GameStages.RACING) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +265,12 @@ public class GameState implements Runnable {
|
||||
case DOWNWIND:
|
||||
playerYacht.turnDownwind();
|
||||
break;
|
||||
case CONTINUOUSLY_TURNING:
|
||||
playerYacht.setContinuouslyTurning(true);
|
||||
break;
|
||||
case DEFAULT_TURNING:
|
||||
playerYacht.setContinuouslyTurning(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.PolarTable;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
/**
|
||||
* A class describing the overall server, which creates and collects server threads for each client
|
||||
@@ -45,29 +37,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
private Thread thread;
|
||||
|
||||
private ServerSocket serverSocket = null;
|
||||
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();;
|
||||
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();
|
||||
private static Integer capacity;
|
||||
private RaceXMLData raceXMLData;
|
||||
private RegattaXMLData regattaXMLData;
|
||||
|
||||
private void startAdvertisingServer() {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
Document doc;
|
||||
XMLGenerator generator = new XMLGenerator();
|
||||
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
String regatta = generator.getRegattaAsXml();
|
||||
StringReader stringReader = new StringReader(regatta);
|
||||
InputSource is = new InputSource(stringReader);
|
||||
doc = db.parse(is);
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
logger.warn("Couldn't load race regatta");
|
||||
return;
|
||||
}
|
||||
|
||||
RegattaXMLData regattaXMLData = XMLParser.parseRegatta(doc);
|
||||
|
||||
|
||||
Integer capacity = GameState.getCapacity();
|
||||
Integer numPlayers = GameState.getNumberOfPlayers();
|
||||
Integer spacesLeft = capacity - numPlayers;
|
||||
@@ -79,33 +54,40 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
|
||||
// Start advertising server
|
||||
try {
|
||||
ServerAdvertiser.getInstance().setMapName(regattaXMLData.getCourseName()).setCapacity(capacity).setNumberOfPlayers(numPlayers);
|
||||
ServerAdvertiser.getInstance().registerGame(PORT, regattaXMLData.getRegattaName());
|
||||
ServerAdvertiser.getInstance()
|
||||
.setMapName(regattaXMLData.getCourseName())
|
||||
.setCapacity(capacity)
|
||||
.setNumberOfPlayers(numPlayers)
|
||||
.registerGame(PORT, regattaXMLData.getRegattaName());
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not register server");
|
||||
}
|
||||
}
|
||||
|
||||
public MainServerThread() {
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
try {
|
||||
serverSocket = new ServerSocket(PORT);
|
||||
} catch (IOException e) {
|
||||
logger.trace("IO error in server thread handler upon trying to make new server socket",
|
||||
0);
|
||||
}
|
||||
|
||||
startAdvertisingServer();
|
||||
|
||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||
GameState.addMessageEventListener(this::broadcastMessage);
|
||||
terminated = false;
|
||||
thread = new Thread(this, "MainServer");
|
||||
startUpdatingWind();
|
||||
startSpawningTokens();
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void startServer() {
|
||||
MessageFactory.updateXMLGenerator(raceXMLData, regattaXMLData);
|
||||
GameState.setRace(raceXMLData);
|
||||
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
|
||||
startAdvertisingServer();
|
||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv"));
|
||||
GameState.addMessageEventListener(this::broadcastMessage);
|
||||
startUpdatingWind();
|
||||
startSpawningTokens();
|
||||
sendSetupMessages();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
@@ -133,8 +115,8 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
} catch (InterruptedException e) {
|
||||
logger.trace("Interrupted exception in Main Server Thread thread sleep", 1);
|
||||
}
|
||||
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState
|
||||
.getCustomizationFlag()) {
|
||||
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState.getCustomizationFlag()) {
|
||||
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
|
||||
sendSetupMessages();
|
||||
GameState.resetCustomizationFlag();
|
||||
}
|
||||
@@ -254,11 +236,28 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
logger.debug("Player Connected From " + serverToClientThread.getThread().getName(), 0);
|
||||
if (serverToClientThreads.size() == 0) { //Sets first client as host.
|
||||
serverToClientThread.setAsHost();
|
||||
serverToClientThread.raceXMLProperty().addListener((obs, oldVal, race) -> {
|
||||
if (race != null) {
|
||||
raceXMLData = race;
|
||||
}
|
||||
if (regattaXMLData != null) {
|
||||
startServer();
|
||||
}
|
||||
});
|
||||
serverToClientThread.regattaXMLProperty().addListener((obs, oldVal, regatta) -> {
|
||||
if (regatta != null) {
|
||||
regattaXMLData = regatta;
|
||||
}
|
||||
if (raceXMLData != null) {
|
||||
startServer();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
serverToClientThread.addConnectionListener(this::sendSetupMessages);
|
||||
MessageFactory.updateBoats(new ArrayList<>(GameState.getYachts().values()));
|
||||
}
|
||||
serverToClientThreads.add(serverToClientThread);
|
||||
serverToClientThread.addConnectionListener(this::sendSetupMessages);
|
||||
serverToClientThread.addDisconnectListener(this::clientDisconnected);
|
||||
|
||||
try {
|
||||
ServerAdvertiser.getInstance().setNumberOfPlayers(GameState.getNumberOfPlayers());
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -17,6 +17,8 @@ import seng302.model.Player;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
@@ -38,6 +40,47 @@ Ideally this class would be created with an instance of the GameState (I tried i
|
||||
public class MessageFactory {
|
||||
|
||||
private static XMLGenerator xmlGenerator = new XMLGenerator();
|
||||
private static XMLMessage race;
|
||||
private static XMLMessage regatta;
|
||||
private static XMLMessage boats;
|
||||
|
||||
public static void updateXMLGenerator(RaceXMLData race, RegattaXMLData regatta) {
|
||||
xmlGenerator.setRegattaTemplate(
|
||||
new RegattaXMLTemplate(
|
||||
regatta.getRegattaName(),
|
||||
regatta.getCourseName(),
|
||||
regatta.getCentralLat(),
|
||||
regatta.getCentralLng()
|
||||
)
|
||||
);
|
||||
xmlGenerator.setRaceTemplate(
|
||||
new RaceXMLTemplate(
|
||||
new ArrayList<>(),
|
||||
new ArrayList<>(),
|
||||
race.getMarkSequence(),
|
||||
race.getCourseLimit(),
|
||||
new ArrayList<>(race.getCompoundMarks().values())
|
||||
)
|
||||
);
|
||||
String xmlStr = xmlGenerator.getRaceAsXml();
|
||||
MessageFactory.race = new XMLMessage(xmlStr, XMLMessageSubType.RACE, xmlStr.length());
|
||||
xmlStr = xmlGenerator.getRegattaAsXml();
|
||||
MessageFactory.regatta = new XMLMessage(xmlStr, XMLMessageSubType.REGATTA, xmlStr.length());
|
||||
xmlStr = xmlGenerator.getBoatsAsXml();
|
||||
MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length());
|
||||
}
|
||||
|
||||
public static void updateBoats(List<ServerYacht> yachts) {
|
||||
xmlGenerator.getRace().setBoats(yachts);
|
||||
String xmlStr = xmlGenerator.getBoatsAsXml();
|
||||
MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length());
|
||||
}
|
||||
|
||||
public static void updateTokens(List<Token> tokens) {
|
||||
xmlGenerator.getRace().setTokens(tokens);
|
||||
String xmlStr = xmlGenerator.getRaceAsXml();
|
||||
MessageFactory.race = new XMLMessage(xmlStr, XMLMessageSubType.RACE, xmlStr.length());
|
||||
}
|
||||
|
||||
|
||||
public static RaceStartStatusMessage getRaceStartStatusMessage() {
|
||||
@@ -98,38 +141,15 @@ public class MessageFactory {
|
||||
}
|
||||
|
||||
public static XMLMessage getRaceXML() {
|
||||
List<ServerYacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||
List<Token> tokens = GameState.getTokensInPlay();
|
||||
RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens);
|
||||
xmlGenerator.setRaceTemplate(raceXMLTemplate);
|
||||
|
||||
XMLMessage raceXMLMessage = new XMLMessage(
|
||||
xmlGenerator.getRaceAsXml(),
|
||||
XMLMessageSubType.RACE,
|
||||
xmlGenerator.getRaceAsXml().length());
|
||||
|
||||
return raceXMLMessage;
|
||||
return race;
|
||||
}
|
||||
|
||||
public static XMLMessage getRegattaXML() {
|
||||
//@TODO calculate lat/lng values
|
||||
|
||||
return new XMLMessage(
|
||||
xmlGenerator.getRegattaAsXml(),
|
||||
XMLMessageSubType.REGATTA,
|
||||
xmlGenerator.getRegattaAsXml().length());
|
||||
return regatta;
|
||||
}
|
||||
|
||||
public static XMLMessage getBoatXML() {
|
||||
List<ServerYacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||
List<Token> tokens = GameState.getTokensInPlay();
|
||||
RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens);
|
||||
xmlGenerator.setRaceTemplate(raceXMLTemplate);
|
||||
|
||||
return new XMLMessage(
|
||||
xmlGenerator.getBoatsAsXml(),
|
||||
XMLMessageSubType.BOAT,
|
||||
xmlGenerator.getBoatsAsXml().length());
|
||||
return boats;
|
||||
}
|
||||
|
||||
public static YachtEventCodeMessage makeCollisionMessage(ServerYacht serverYacht) {
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
@@ -25,15 +22,16 @@ import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RegistrationResponseMessage;
|
||||
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||
import seng302.gameServer.messages.XMLMessage;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.packets.PacketType;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.StreamParser;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
/**
|
||||
* A class describing a single connection to a Client for the purposes of sending and receiving on
|
||||
@@ -81,6 +79,9 @@ public class ServerToClientThread implements Runnable {
|
||||
private ServerYacht yacht;
|
||||
private Player player;
|
||||
|
||||
private SimpleObjectProperty<RaceXMLData> raceXMLProperty = new SimpleObjectProperty<>();
|
||||
private SimpleObjectProperty<RegattaXMLData> regattaXMLProperty = new SimpleObjectProperty<>();
|
||||
|
||||
public ServerToClientThread(Socket socket) {
|
||||
this.socket = socket;
|
||||
seqNo = 0;
|
||||
@@ -101,30 +102,8 @@ public class ServerToClientThread implements Runnable {
|
||||
}
|
||||
|
||||
private void setUpPlayer(){
|
||||
BufferedReader fn;
|
||||
String fName = "";
|
||||
BufferedReader ln;
|
||||
String fName = "Player" + GameState.getNumberOfPlayers().toString();
|
||||
String lName = "";
|
||||
|
||||
fn = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
ServerToClientThread.class.getResourceAsStream(
|
||||
"/server_config/CSV_Database_of_First_Names.csv"
|
||||
)
|
||||
)
|
||||
);
|
||||
List<String> all = fn.lines().collect(Collectors.toList());
|
||||
fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||
ln = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
ServerToClientThread.class.getResourceAsStream(
|
||||
"/server_config/CSV_Database_of_Last_Names.csv"
|
||||
)
|
||||
)
|
||||
);
|
||||
all = ln.lines().collect(Collectors.toList());
|
||||
lName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||
|
||||
ServerYacht yacht = new ServerYacht(
|
||||
BoatMeshType.DINGHY, sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ"
|
||||
);
|
||||
@@ -186,37 +165,51 @@ public class ServerToClientThread implements Runnable {
|
||||
long computedCrc = checksum.getValue();
|
||||
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||
if (computedCrc == packetCrc) {
|
||||
StreamPacket packet = new StreamPacket(type, payloadLength, timeStamp, payload);
|
||||
switch (PacketType.assignPacketType(type, payload)) {
|
||||
case BOAT_ACTION:
|
||||
BoatAction actionType = ServerPacketParser
|
||||
.extractBoatAction(
|
||||
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
BoatAction actionType = ServerPacketParser.extractBoatAction(packet);
|
||||
GameState.updateBoat(sourceId, actionType);
|
||||
break;
|
||||
|
||||
case RACE_REGISTRATION_REQUEST:
|
||||
ClientType requestedType = ServerPacketParser.extractClientType(
|
||||
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
|
||||
ClientType requestedType = ServerPacketParser
|
||||
.extractClientType(packet);
|
||||
completeRegistration(requestedType);
|
||||
break;
|
||||
case CHATTER_TEXT:
|
||||
ChatterMessage chatterMessage = ServerPacketParser
|
||||
.extractChatterText(
|
||||
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
.extractChatterText(packet);
|
||||
GameState.processChatter(chatterMessage, isHost);
|
||||
break;
|
||||
case RACE_CUSTOMIZATION_REQUEST:
|
||||
Long sourceID = Message
|
||||
.bytesToLong(Arrays.copyOfRange(payload, 0, 3));
|
||||
Long sourceID = Message.bytesToLong(
|
||||
Arrays.copyOfRange(payload, 0, 3)
|
||||
);
|
||||
CustomizeRequestType requestType = ServerPacketParser
|
||||
.extractCustomizationType(
|
||||
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
.extractCustomizationType(packet);
|
||||
|
||||
GameState.customizePlayer(sourceID, requestType,
|
||||
Arrays.copyOfRange(payload, 6, payload.length));
|
||||
Arrays.copyOfRange(payload, 6, payload.length)
|
||||
);
|
||||
GameState.setCustomizationFlag();
|
||||
// TODO: 17/08/2017 ajm412: Send a response packet here, not really necessary until we do shapes.
|
||||
break;
|
||||
case RACE_XML:
|
||||
raceXMLProperty.set(
|
||||
XMLParser.parseRace(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case REGATTA_XML:
|
||||
regattaXMLProperty.set(
|
||||
XMLParser.parseRegatta(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
logger.warn("Packet has been dropped", 1);
|
||||
@@ -233,23 +226,9 @@ public class ServerToClientThread implements Runnable {
|
||||
}
|
||||
|
||||
public void sendSetupMessages() {
|
||||
xmlGenerator = new XMLGenerator();
|
||||
RaceXMLTemplate race = new RaceXMLTemplate(new ArrayList<>(GameState.getYachts().values()), new ArrayList<>());
|
||||
|
||||
xmlGenerator.setRaceTemplate(race);
|
||||
|
||||
XMLMessage xmlMessage;
|
||||
xmlMessage = new XMLMessage(xmlGenerator.getRegattaAsXml(), XMLMessageSubType.REGATTA,
|
||||
xmlGenerator.getRegattaAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
|
||||
xmlMessage = new XMLMessage(xmlGenerator.getBoatsAsXml(), XMLMessageSubType.BOAT,
|
||||
xmlGenerator.getBoatsAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
|
||||
xmlMessage = new XMLMessage(xmlGenerator.getRaceAsXml(), XMLMessageSubType.RACE,
|
||||
xmlGenerator.getRaceAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
sendMessage(MessageFactory.getRegattaXML());
|
||||
sendMessage(MessageFactory.getBoatXML());
|
||||
sendMessage(MessageFactory.getRaceXML());
|
||||
}
|
||||
|
||||
private void closeSocket() {
|
||||
@@ -345,4 +324,12 @@ public class ServerToClientThread implements Runnable {
|
||||
public void setAsHost() {
|
||||
isHost = true;
|
||||
}
|
||||
|
||||
public SimpleObjectProperty<RaceXMLData> raceXMLProperty() {
|
||||
return raceXMLProperty;
|
||||
}
|
||||
|
||||
public SimpleObjectProperty<RegattaXMLData> regattaXMLProperty() {
|
||||
return regattaXMLProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ public enum BoatAction {
|
||||
TACK_GYBE(4),
|
||||
UPWIND(5),
|
||||
DOWNWIND(6),
|
||||
MAINTAIN_HEADING(7);
|
||||
MAINTAIN_HEADING(7),
|
||||
CONTINUOUSLY_TURNING(8),
|
||||
DEFAULT_TURNING(9);
|
||||
|
||||
private final int type;
|
||||
private static final Map<Integer, BoatAction> intToTypeMap = new HashMap<>();
|
||||
|
||||
@@ -5,19 +5,19 @@ package seng302.gameServer.messages;
|
||||
*/
|
||||
public class BoatActionMessage extends Message{
|
||||
private final MessageType MESSAGE_TYPE = MessageType.BOAT_ACTION;
|
||||
private final int MESSAGE_SIZE = 1;
|
||||
private final int MESSAGE_SIZE = 5;
|
||||
private BoatAction actionType;
|
||||
|
||||
public BoatActionMessage(BoatAction actionType) {
|
||||
public BoatActionMessage(BoatAction actionType, int sourceId) {
|
||||
this.actionType = actionType;
|
||||
setHeader(new Header(MessageType.BOAT_ACTION, 0, (short) 1)); // the second variable is the source id
|
||||
setHeader(new Header(MessageType.BOAT_ACTION, sourceId, (short) MESSAGE_SIZE)); // the second variable is the source id
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
// Write message fields
|
||||
putInt(actionType.getValue(), 1);
|
||||
putInt(sourceId, 4);
|
||||
writeCRC();
|
||||
rewind();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,8 +4,8 @@ package seng302.gameServer.messages;
|
||||
public class RegistrationRequestMessage extends Message {
|
||||
private static int MESSAGE_LENGTH = 2;
|
||||
|
||||
public RegistrationRequestMessage(ClientType type){
|
||||
setHeader(new Header(MessageType.REGISTRATION_REQUEST, 1, (short) getSize()));
|
||||
public RegistrationRequestMessage(ClientType type, int clientID){
|
||||
setHeader(new Header(MessageType.REGISTRATION_REQUEST, clientID, (short) getSize()));
|
||||
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package seng302.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javafx.scene.input.KeyCode;
|
||||
|
||||
public class GameKeyBind {
|
||||
|
||||
private static GameKeyBind instance;
|
||||
private Map<KeyCode, KeyAction> keyToActionMap;
|
||||
private Map<KeyAction, KeyCode> actionToKeyMap;
|
||||
private Boolean continuouslyTurning;
|
||||
|
||||
|
||||
private GameKeyBind() {
|
||||
setToDefault();
|
||||
}
|
||||
|
||||
public void setToDefault() {
|
||||
actionToKeyMap = new HashMap<>();
|
||||
keyToActionMap = new HashMap<>();
|
||||
continuouslyTurning = false;
|
||||
// default key bindings
|
||||
ArrayList<KeyCode> keys = new ArrayList<>();
|
||||
keys.add(KeyCode.Z);
|
||||
keys.add(KeyCode.X);
|
||||
keys.add(KeyCode.SPACE);
|
||||
keys.add(KeyCode.SHIFT);
|
||||
keys.add(KeyCode.ENTER);
|
||||
keys.add(KeyCode.PAGE_UP);
|
||||
keys.add(KeyCode.PAGE_DOWN);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
actionToKeyMap.put(KeyAction.getType(i + 1), keys.get(i));
|
||||
keyToActionMap.put(keys.get(i), KeyAction.getType(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static GameKeyBind getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new GameKeyBind();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public KeyCode getKeyCode(KeyAction keyAction) {
|
||||
return instance.actionToKeyMap.get(keyAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a key to a key action
|
||||
*
|
||||
* @return true if successfully bind
|
||||
*/
|
||||
public boolean bindKeyToAction(KeyCode keyCode, KeyAction keyAction) {
|
||||
if (instance.keyToActionMap.containsKey(keyCode)) {
|
||||
// if the key has been bound to other action, return false
|
||||
return false;
|
||||
} else {
|
||||
instance.keyToActionMap.put(keyCode, keyAction); // add key -> action
|
||||
KeyCode oldKeyCode = instance.actionToKeyMap
|
||||
.get(keyAction); // get old key for the action
|
||||
instance.keyToActionMap.remove(oldKeyCode); // remove the old key -> action
|
||||
instance.actionToKeyMap
|
||||
.replace(keyAction, keyCode); // replace the old key by the newer one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleTurningMode() {
|
||||
continuouslyTurning = !continuouslyTurning;
|
||||
}
|
||||
|
||||
public Boolean isContinuouslyTurning() {
|
||||
return continuouslyTurning;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package seng302.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum KeyAction {
|
||||
ZOOM_IN(1),
|
||||
ZOOM_OUT(2),
|
||||
VMG(3),
|
||||
SAILS_STATE(4),
|
||||
TACK_GYBE(5),
|
||||
UPWIND(6),
|
||||
DOWNWIND(7);
|
||||
|
||||
private final int type;
|
||||
private static final Map<Integer, KeyAction> intToTypeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (KeyAction type : KeyAction.values()) {
|
||||
intToTypeMap.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
KeyAction(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static KeyAction getType(int value) {
|
||||
return intToTypeMap.get(value);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,9 @@ public class Limit extends GeoPoint {
|
||||
public Integer getSeqID() {
|
||||
return seqID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Limit = {seqID=" + seqID + ", lat=" + getLat() + ", lng=" + getLng() + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package seng302.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import javafx.geometry.Point2D;
|
||||
import seng302.utilities.GeoUtility;
|
||||
|
||||
/**
|
||||
* Contains information on a scaled lat lon point for use with mapping geographical elements to a 2d plane.
|
||||
*/
|
||||
public class ScaledPoint extends GeoPoint {
|
||||
|
||||
public enum ScaleDirection {
|
||||
HORIZONTAL,
|
||||
VERTICAL
|
||||
}
|
||||
|
||||
private double x, y, scaleFactor;
|
||||
private ScaleDirection scaleDirection;
|
||||
|
||||
private ScaledPoint(double lat, double lng, double x, double y, double scaleFactor, ScaleDirection direction) {
|
||||
super(lat, lng);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.scaleFactor = scaleFactor;
|
||||
this.scaleDirection = direction;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getScaleFactor() {
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
public ScaleDirection getScaleDirection() {
|
||||
return scaleDirection;
|
||||
}
|
||||
|
||||
public Point2D findScaledXY(GeoPoint unscaled) {
|
||||
return findScaledXY(unscaled.getLat(), unscaled.getLng());
|
||||
}
|
||||
|
||||
public Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
||||
double distanceFromReference;
|
||||
double angleFromReference;
|
||||
double xReference = this.getX();
|
||||
double yReference = this.getY();
|
||||
|
||||
angleFromReference = GeoUtility.getBearingRad(
|
||||
this, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
distanceFromReference = GeoUtility.getDistance(
|
||||
this, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||
xReference += scaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
yReference -= scaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
} else if (angleFromReference >= 0) {
|
||||
angleFromReference = angleFromReference - Math.PI / 2;
|
||||
xReference += scaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
yReference += scaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||
angleFromReference = Math.abs(angleFromReference);
|
||||
xReference -= scaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
yReference -= scaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
} else {
|
||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||
xReference -= scaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
yReference += scaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
}
|
||||
return new Point2D(xReference, yReference);
|
||||
}
|
||||
|
||||
public static ScaledPoint makeScaledPoint(double width, double height,
|
||||
List<? extends GeoPoint> points, boolean centered) {
|
||||
|
||||
double referencePointX, referencePointY, scaleFactor, lat, lng;
|
||||
ScaleDirection scaleDirection;
|
||||
points = new ArrayList<>(points);
|
||||
points.sort(Comparator.comparingDouble(GeoPoint::getLat));
|
||||
GeoPoint minLatPoint = points.get(0);
|
||||
GeoPoint maxLatPoint = points.get(points.size() - 1);
|
||||
|
||||
points.sort(Comparator.comparingDouble(GeoPoint::getLng));
|
||||
GeoPoint minLonPoint = points.get(0);
|
||||
GeoPoint maxLonPoint = points.get(points.size() - 1);
|
||||
|
||||
referencePointX = centered ? 0 : width / 2;
|
||||
referencePointY = centered ? 0 : height / 2;
|
||||
|
||||
lat = (maxLatPoint.getLat() - minLatPoint.getLat()) / 2 + minLatPoint.getLat();
|
||||
lng = (maxLonPoint.getLng() - minLonPoint.getLng()) / 2 + minLonPoint.getLng();
|
||||
|
||||
GeoPoint ref = new GeoPoint(lat, lng);
|
||||
|
||||
double vertDistance = GeoUtility.getDistance(
|
||||
ref, new GeoPoint(ref.getLat(), maxLonPoint.getLng())
|
||||
) * 2.1;
|
||||
|
||||
double horiDistance = GeoUtility.getDistance(
|
||||
ref, new GeoPoint(maxLatPoint.getLat(), ref.getLng())
|
||||
) * 2.1;
|
||||
|
||||
double vertScale = height / vertDistance;
|
||||
|
||||
if (horiDistance * vertScale > width) {
|
||||
scaleFactor = width / horiDistance;
|
||||
scaleDirection = ScaleDirection.HORIZONTAL;
|
||||
} else {
|
||||
scaleFactor = vertScale;
|
||||
scaleDirection = ScaleDirection.VERTICAL;
|
||||
}
|
||||
return new ScaledPoint(lat, lng, referencePointX, referencePointY, scaleFactor, scaleDirection);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package seng302.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -10,12 +11,6 @@ import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
/**
|
||||
* Yacht class for the racing boat. <p> Class created to store more variables (eg. boat statuses)
|
||||
* compared to the XMLParser boat class, also done outside Boat class because some old variables are
|
||||
@@ -61,6 +56,8 @@ public class ServerYacht {
|
||||
private TokenType powerUp;
|
||||
private Long powerUpStartTime;
|
||||
|
||||
//turning mode
|
||||
private Boolean continuouslyTurning;
|
||||
|
||||
public ServerYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName,
|
||||
String boatName, String country) {
|
||||
@@ -85,6 +82,8 @@ public class ServerYacht {
|
||||
this.hasEnteredRoundingZone = false;
|
||||
this.hasPassedLine = false;
|
||||
this.hasPassedThroughGate = false;
|
||||
|
||||
this.continuouslyTurning = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,6 +191,9 @@ public class ServerYacht {
|
||||
public void turnUpwind() {
|
||||
disableAutoPilot();
|
||||
Double normalizedHeading = normalizeHeading();
|
||||
if (continuouslyTurning) {
|
||||
adjustHeading(turnStep);
|
||||
} else {
|
||||
if (normalizedHeading == 0) {
|
||||
if (lastHeading < 180) {
|
||||
adjustHeading(-turnStep);
|
||||
@@ -210,10 +212,14 @@ public class ServerYacht {
|
||||
adjustHeading(turnStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void turnDownwind() {
|
||||
disableAutoPilot();
|
||||
Double normalizedHeading = normalizeHeading();
|
||||
if (continuouslyTurning) {
|
||||
adjustHeading(-turnStep);
|
||||
} else {
|
||||
if (normalizedHeading == 0) {
|
||||
if (lastHeading < 180) {
|
||||
adjustHeading(turnStep);
|
||||
@@ -232,6 +238,7 @@ public class ServerYacht {
|
||||
adjustHeading(-turnStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the VMG from the polartable for upwind or downwind depending on the boats direction,
|
||||
@@ -445,4 +452,8 @@ public class ServerYacht {
|
||||
public BoatMeshType getBoatType() {
|
||||
return boatType;
|
||||
}
|
||||
|
||||
public void setContinuouslyTurning(Boolean continuouslyTurning) {
|
||||
this.continuouslyTurning = continuouslyTurning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package seng302.model.mark;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.GeoPoint;
|
||||
@@ -10,13 +10,13 @@ public class CompoundMark {
|
||||
|
||||
private int compoundMarkId;
|
||||
private String name;
|
||||
private List<Mark> marks = new ArrayList<>();
|
||||
private List<Mark> marks;
|
||||
private GeoPoint midPoint;
|
||||
|
||||
public CompoundMark(int markID, String name, List<Mark> marks) {
|
||||
this.compoundMarkId = markID;
|
||||
this.name = name;
|
||||
this.marks.addAll(marks);
|
||||
this.marks = Collections.unmodifiableList(marks);
|
||||
if (marks.size() > 1) {
|
||||
this.midPoint = GeoUtility.getDirtyMidPoint(marks.get(0), marks.get(1));
|
||||
} else {
|
||||
|
||||
@@ -32,4 +32,10 @@ public class Corner {
|
||||
public Integer getZoneSize() {
|
||||
return zoneSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Corner = {seqID=" + seqID + ", compoundMarkID=" + compoundMarkID + ", rounding="
|
||||
+ rounding +", zoneSize=" + zoneSize + "}";
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
package seng302.model.mark;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
import seng302.utilities.XMLParser;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class to hold the order of the marks in the race.
|
||||
@@ -25,10 +14,17 @@ import java.util.*;
|
||||
public class MarkOrder {
|
||||
private List<CompoundMark> raceMarkOrder;
|
||||
private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
|
||||
private Set<Mark> allMarks;
|
||||
|
||||
public MarkOrder(){
|
||||
loadRaceProperties();
|
||||
|
||||
public MarkOrder(RaceXMLData raceXMLData){
|
||||
raceMarkOrder = new ArrayList<>();
|
||||
for (Corner corner : raceXMLData.getMarkSequence()){
|
||||
CompoundMark compoundMark = raceXMLData.getCompoundMarks().get(corner.getCompoundMarkID());
|
||||
compoundMark.setRoundingSide(
|
||||
RoundingSide.getRoundingSide(corner.getRounding())
|
||||
);
|
||||
raceMarkOrder.add(compoundMark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +36,6 @@ public class MarkOrder {
|
||||
logger.warn("Race order accessed but not instantiated");
|
||||
return null;
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(raceMarkOrder);
|
||||
}
|
||||
|
||||
@@ -74,70 +69,4 @@ public class MarkOrder {
|
||||
public CompoundMark getNextMark(Integer currentSeqID) throws IndexOutOfBoundsException {
|
||||
return raceMarkOrder.get(currentSeqID + 1);
|
||||
}
|
||||
|
||||
public Set<Mark> getAllMarks(){
|
||||
return Collections.unmodifiableSet(allMarks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the race order from an XML string
|
||||
* @param xml An AC35 RaceXML
|
||||
* @return An ordered list of marks in the race
|
||||
*/
|
||||
private List<CompoundMark> loadRaceOrderFromXML(String xml) {
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
Document doc;
|
||||
allMarks = new HashSet<>();
|
||||
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(new InputSource(new StringReader(xml)));
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
logger.error("Failed to read generated race XML");
|
||||
return null;
|
||||
}
|
||||
|
||||
RaceXMLData data = XMLParser.parseRace(doc);
|
||||
|
||||
if (data != null){
|
||||
logger.debug("Loaded RaceXML for mark order");
|
||||
List<Corner> corners = data.getMarkSequence();
|
||||
Map<Integer, CompoundMark> marks = data.getCompoundMarks();
|
||||
List<CompoundMark> course = new ArrayList<>();
|
||||
for (Corner corner : corners){
|
||||
CompoundMark compoundMark = marks.get(corner.getCompoundMarkID());
|
||||
compoundMark.setRoundingSide(
|
||||
RoundingSide.getRoundingSide(corner.getRounding())
|
||||
);
|
||||
course.add(compoundMark);
|
||||
allMarks.addAll(compoundMark.getMarks());
|
||||
}
|
||||
|
||||
return course;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the raceXML and mark order
|
||||
*/
|
||||
private void loadRaceProperties(){
|
||||
XMLGenerator generator = new XMLGenerator();
|
||||
|
||||
// TODO: 29/08/17 wmu16 - This is terrible, having to make a template just to receive constant data
|
||||
generator.setRaceTemplate(new RaceXMLTemplate(
|
||||
new ArrayList<>(),
|
||||
new ArrayList<>()));
|
||||
|
||||
String raceXML = generator.getRaceAsXml();
|
||||
|
||||
if (raceXML == null){
|
||||
logger.error("Failed to generate raceXML (for race properties)");
|
||||
return;
|
||||
}
|
||||
raceMarkOrder = loadRaceOrderFromXML(raceXML);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package seng302.model.stream.xml.generator;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.token.Token;
|
||||
|
||||
/**
|
||||
@@ -15,10 +17,17 @@ public class RaceXMLTemplate {
|
||||
private List<ServerYacht> yachts;
|
||||
private LocalDateTime startTime;
|
||||
private List<Token> tokens;
|
||||
private List<Corner> roundings;
|
||||
private List<Limit> courseLimit;
|
||||
private List<CompoundMark> course;
|
||||
|
||||
public RaceXMLTemplate(List<ServerYacht> yachts, List<Token> tokens) {
|
||||
public RaceXMLTemplate(List<ServerYacht> yachts, List<Token> tokens, List<Corner> roundings,
|
||||
List<Limit> limit, List<CompoundMark> course) {
|
||||
this.yachts = yachts;
|
||||
this.tokens = tokens;
|
||||
this.roundings = roundings;
|
||||
this.courseLimit = limit;
|
||||
this.course = course;
|
||||
startTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@@ -39,6 +48,18 @@ public class RaceXMLTemplate {
|
||||
return Collections.unmodifiableList(tokens);
|
||||
}
|
||||
|
||||
public List<CompoundMark> getCompoundMarks() {
|
||||
return Collections.unmodifiableList(course);
|
||||
}
|
||||
|
||||
public List<Limit> getCourseLimit() {
|
||||
return Collections.unmodifiableList(courseLimit);
|
||||
}
|
||||
|
||||
public List<Corner> getRoundings() {
|
||||
return Collections.unmodifiableList(roundings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time until the race starts
|
||||
* @param seconds The time in seconds until the race starts
|
||||
@@ -54,4 +75,12 @@ public class RaceXMLTemplate {
|
||||
public String getRaceStartTime(){
|
||||
return startTime.toString();
|
||||
}
|
||||
|
||||
public void setBoats(List<ServerYacht> boats) {
|
||||
yachts = boats;
|
||||
}
|
||||
|
||||
public void setTokens(List<Token> tokens) {
|
||||
this.tokens = tokens;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,4 +179,8 @@ public class XMLGenerator {
|
||||
public RegattaXMLTemplate getRegatta() {
|
||||
return regatta;
|
||||
}
|
||||
|
||||
public RaceXMLTemplate getRace() {
|
||||
return race;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,29 @@
|
||||
package seng302.utilities;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.util.Pair;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.model.Colors;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.model.token.Token;
|
||||
@@ -139,14 +149,26 @@ public class XMLParser {
|
||||
Node currentBoat = boatsList.item(i);
|
||||
if (currentBoat.getNodeName().equals("Boat")) {
|
||||
// Boat boat = new Boat(currentBoat);
|
||||
BoatMeshType boatMeshType;
|
||||
try {
|
||||
boatMeshType = BoatMeshType.valueOf(XMLParser.getNodeAttributeString(currentBoat, "Type"));
|
||||
} catch (IllegalArgumentException e){
|
||||
boatMeshType = BoatMeshType.DINGHY;
|
||||
}
|
||||
Color color;
|
||||
try {
|
||||
color = Color.web(getNodeAttributeString(currentBoat, "Color"));
|
||||
} catch (NullPointerException npe) {
|
||||
color = Colors.getColor(new Random().nextInt(8));
|
||||
}
|
||||
ClientYacht yacht = new ClientYacht(
|
||||
BoatMeshType.valueOf(XMLParser.getNodeAttributeString(currentBoat, "Type")),
|
||||
boatMeshType,
|
||||
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
|
||||
XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
|
||||
XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
|
||||
XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
|
||||
XMLParser.getNodeAttributeString(currentBoat, "Country"));
|
||||
yacht.setColour(Color.web(getNodeAttributeString(currentBoat, "Color")));
|
||||
yacht.setColour(color);
|
||||
competingBoats.put(yacht.getSourceId(), yacht);
|
||||
}
|
||||
}
|
||||
@@ -182,7 +204,7 @@ public class XMLParser {
|
||||
public static RaceXMLData parseRace(Document doc) {
|
||||
Element docEle = doc.getDocumentElement();
|
||||
return new RaceXMLData(
|
||||
extractParticpantIDs(docEle),
|
||||
extractParticipantIDs(docEle),
|
||||
extractTokens(docEle),
|
||||
extractCompoundMarks(docEle),
|
||||
extractMarkOrder(docEle),
|
||||
@@ -195,6 +217,7 @@ public class XMLParser {
|
||||
*/
|
||||
private static List<Token> extractTokens(Element docEle) {
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
try {
|
||||
NodeList tokenList = docEle.getElementsByTagName("Tokens").item(0).getChildNodes();
|
||||
for (int i = 0; i < tokenList.getLength(); i++) {
|
||||
Node tokenNode = tokenList.item(i);
|
||||
@@ -205,7 +228,9 @@ public class XMLParser {
|
||||
tokens.add(new Token(TokenType.valueOf(tokenType), lat, lng));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NullPointerException npe) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@@ -218,13 +243,11 @@ public class XMLParser {
|
||||
for (int i = 0; i < limitList.getLength(); i++) {
|
||||
Node limitNode = limitList.item(i);
|
||||
if (limitNode.getNodeName().equals("Limit")) {
|
||||
courseLimit.add(
|
||||
new Limit(
|
||||
courseLimit.add(new Limit(
|
||||
XMLParser.getNodeAttributeInt(limitNode, "SeqID"),
|
||||
XMLParser.getNodeAttributeDouble(limitNode, "Lat"),
|
||||
XMLParser.getNodeAttributeDouble(limitNode, "Lon")
|
||||
)
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
return courseLimit;
|
||||
@@ -256,7 +279,7 @@ public class XMLParser {
|
||||
/**
|
||||
* Extracts course participants data
|
||||
*/
|
||||
private static List<Integer> extractParticpantIDs (Element docEle) {
|
||||
private static List<Integer> extractParticipantIDs(Element docEle) {
|
||||
List<Integer> boatIDs = new ArrayList<>();
|
||||
NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes();
|
||||
for (int i = 0; i < pList.getLength(); i++) {
|
||||
@@ -278,10 +301,11 @@ public class XMLParser {
|
||||
for (int i = 0; i < cMarkList.getLength(); i++) {
|
||||
Node cMarkNode = cMarkList.item(i);
|
||||
if (cMarkNode.getNodeName().equals("CompoundMark")) {
|
||||
String name = XMLParser.getNodeAttributeString(cMarkNode, "Name");
|
||||
name = (name == null || name.equals("")) ? "Mark " + i+1: name;
|
||||
cMark = new CompoundMark(
|
||||
XMLParser.getNodeAttributeInt(cMarkNode, "CompoundMarkID"),
|
||||
XMLParser.getNodeAttributeString(cMarkNode, "Name"),
|
||||
createMarks(cMarkNode)
|
||||
name, createMarks(cMarkNode)
|
||||
);
|
||||
allMarks.add(cMark);
|
||||
}
|
||||
@@ -302,14 +326,156 @@ public class XMLParser {
|
||||
Node markNode = childMarks.item(i);
|
||||
if (markNode.getNodeName().equals("Mark")) {
|
||||
Integer seqID = XMLParser.getNodeAttributeInt(markNode, "SeqID");
|
||||
seqID = (seqID == null) ? i+1 : seqID;
|
||||
|
||||
Integer sourceID = XMLParser.getNodeAttributeInt(markNode, "SourceID");
|
||||
sourceID = (sourceID == null) ? i+1 : sourceID;
|
||||
|
||||
String markName = XMLParser.getNodeAttributeString(markNode, "Name");
|
||||
markName = (markName == null || markName.equals("")) ? cMarkName + " " + i+1: markName;
|
||||
|
||||
Double targetLat = XMLParser.getNodeAttributeDouble(markNode, "TargetLat");
|
||||
Double targetLng = XMLParser.getNodeAttributeDouble(markNode, "TargetLng");
|
||||
|
||||
Mark mark = new Mark(markName, seqID, targetLat, targetLng, sourceID);
|
||||
subMarks.add(mark);
|
||||
}
|
||||
}
|
||||
return subMarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* This ungodly combination of existing methods and code blocks parses a race definition file.
|
||||
* Look upon it and despair.
|
||||
* @param url the location of the race def file
|
||||
* @param serverName the name of the server
|
||||
* @param repetitions the repetitions of a segment of the race def file.
|
||||
* @return a pair which contains regatta string, race string as key, value pair.
|
||||
*/
|
||||
public static Pair<String, String> parseRaceDef(String url, String serverName, int repetitions) {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
Document doc = null;
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(url);
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Element docEle = doc.getDocumentElement();
|
||||
|
||||
RegattaXMLTemplate regattaXMLTemplate = new RegattaXMLTemplate(
|
||||
serverName, XMLParser.getElementString(docEle, "CourseName"),
|
||||
XMLParser.getElementDouble(docEle, "CentralLat"),
|
||||
XMLParser.getElementDouble(docEle, "CentralLng")
|
||||
);
|
||||
XMLGenerator xmlGenerator = new XMLGenerator();
|
||||
xmlGenerator.setRegattaTemplate(regattaXMLTemplate);
|
||||
|
||||
RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(new ArrayList<>(), new ArrayList<>(),
|
||||
XMLParser.extractMarkOrderRaceDef(docEle, repetitions),
|
||||
XMLParser.extractCourseLimitRaceDef(docEle),
|
||||
XMLParser.extractCompoundMarksRaceDef(docEle)
|
||||
);
|
||||
xmlGenerator.setRaceTemplate(raceXMLTemplate);
|
||||
return new Pair<>(xmlGenerator.getRegattaAsXml(), xmlGenerator.getRaceAsXml());
|
||||
}
|
||||
|
||||
private static List<Corner> extractMarkOrderRaceDef(Element docEle, int repitions){
|
||||
List<Corner> compoundMarkSequence = new ArrayList<>();
|
||||
NodeList cornerList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
|
||||
|
||||
int seqId = 1;
|
||||
final int zoneSize = 3;
|
||||
|
||||
for (int i=0; i<cornerList.getLength(); i++) {
|
||||
Node segment = cornerList.item(i);
|
||||
if (segment.getNodeName().equals("OpeningSegment") ||
|
||||
segment.getNodeName().equals("ClosingSegment")) {
|
||||
|
||||
seqId = parseCourseSegment(segment, seqId, compoundMarkSequence);
|
||||
|
||||
} else if (segment.getNodeName().equals("RepeatingSegment")) {
|
||||
for (int k = 0; k < repitions; k++) {
|
||||
seqId = parseCourseSegment(segment, seqId, compoundMarkSequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
return compoundMarkSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a segment of the course adding new Corners to the given list.
|
||||
* @param segment Segment to parse
|
||||
* @param seqID initial sequence ID
|
||||
* @param course course to add corners to
|
||||
* @return the last sequence id.
|
||||
*/
|
||||
private static int parseCourseSegment(Node segment, int seqID, List<Corner> course) {
|
||||
NodeList segmentList = segment.getChildNodes();
|
||||
for (int j = 0; j < segmentList.getLength(); j++) {
|
||||
Node corner = segmentList.item(j);
|
||||
if (corner.getNodeName().equals("Corner")) {
|
||||
String rounding = XMLParser.getNodeAttributeString(corner, "Rounding");
|
||||
rounding = //Converting "P" to "Port" and "S" to "Stbd"
|
||||
rounding.equals("P") ? "Port" :
|
||||
rounding.equals("S") ? "Stbd" : rounding;
|
||||
course.add(new Corner(
|
||||
seqID++, XMLParser.getNodeAttributeInt(corner, "CompoundMarkID"),
|
||||
rounding, 3
|
||||
));
|
||||
}
|
||||
}
|
||||
return seqID;
|
||||
}
|
||||
|
||||
private static List<Limit> extractCourseLimitRaceDef(Element docEle) {
|
||||
List<Limit> courseLimit = new ArrayList<>();
|
||||
NodeList limitList = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes();
|
||||
int seqId = 1;
|
||||
for (int i = 0; i < limitList.getLength(); i++) {
|
||||
Node limitNode = limitList.item(i);
|
||||
if (limitNode.getNodeName().equals("Limit")) {
|
||||
courseLimit.add(new Limit(
|
||||
seqId++, XMLParser.getNodeAttributeDouble(limitNode, "Lat"),
|
||||
XMLParser.getNodeAttributeDouble(limitNode, "Lng")
|
||||
));
|
||||
}
|
||||
}
|
||||
return courseLimit;
|
||||
}
|
||||
|
||||
private static List<CompoundMark> extractCompoundMarksRaceDef(Element docEle){
|
||||
List<CompoundMark> allMarks = new ArrayList<>();
|
||||
NodeList cMarkList = docEle.getElementsByTagName("Marks").item(0).getChildNodes();
|
||||
CompoundMark cMark;
|
||||
int markCount = 200;
|
||||
for (int i = 0; i < cMarkList.getLength(); i++) {
|
||||
Node cMarkNode = cMarkList.item(i);
|
||||
if (cMarkNode.getNodeName().equals("CompoundMark")) {
|
||||
Integer id = XMLParser.getNodeAttributeInt(cMarkNode, "CompoundMarkID");
|
||||
List<Mark> subMarks = createMarksRaceDef(cMarkNode, markCount,"Mark " + id);
|
||||
markCount += subMarks.size();
|
||||
allMarks.add(new CompoundMark(id, "Mark " + id, subMarks));
|
||||
}
|
||||
}
|
||||
return allMarks;
|
||||
}
|
||||
|
||||
private static List<Mark> createMarksRaceDef(Node compoundMark, int markCount, String markName) {
|
||||
List<Mark> subMarks = new ArrayList<>();
|
||||
NodeList childMarks = compoundMark.getChildNodes();
|
||||
int seqID = 1;
|
||||
for (int i = 0; i < childMarks.getLength(); i++) {
|
||||
Node markNode = childMarks.item(i);
|
||||
if (markNode.getNodeName().equals("Mark")) {
|
||||
Double targetLat = XMLParser.getNodeAttributeDouble(markNode, "Lat");
|
||||
Double targetLng = XMLParser.getNodeAttributeDouble(markNode, "Lng");
|
||||
Mark mark = new Mark(markName + " subMark " + seqID, seqID, targetLat, targetLng, markCount++);
|
||||
subMarks.add(mark);
|
||||
seqID += 1;
|
||||
}
|
||||
}
|
||||
return subMarks;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
import javafx.util.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
@@ -25,8 +26,11 @@ import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RegistrationRequestMessage;
|
||||
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||
import seng302.gameServer.messages.XMLMessage;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.model.stream.packets.PacketType;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
/**
|
||||
* A class describing a single connection to a Server for the purposes of sending and receiving on
|
||||
@@ -44,7 +48,7 @@ public class ClientToServerThread implements Runnable {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DisconnectedFromHostListener {
|
||||
void notifYDisconnection (String message);
|
||||
void notifyDisconnection(String message);
|
||||
}
|
||||
|
||||
private class ByteReadException extends Exception {
|
||||
@@ -68,7 +72,7 @@ public class ClientToServerThread implements Runnable {
|
||||
private Timer upWindPacketTimer = new Timer();
|
||||
private Timer downWindPacketTimer = new Timer();
|
||||
private boolean upwindTimerFlag = false, downwindTimerFlag = false;
|
||||
static public final int PACKET_SENDING_INTERVAL_MS = 100;
|
||||
public static final int PACKET_SENDING_INTERVAL_MS = 100;
|
||||
|
||||
private int clientId = -1;
|
||||
|
||||
@@ -166,7 +170,7 @@ public class ClientToServerThread implements Runnable {
|
||||
private void notifyDisconnectListeners (String message) {
|
||||
if (socketOpen) {
|
||||
for (DisconnectedFromHostListener listener : disconnectionListeners) {
|
||||
listener.notifYDisconnection(message);
|
||||
listener.notifyDisconnection(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,7 +179,7 @@ public class ClientToServerThread implements Runnable {
|
||||
* Sends a request to the server asking for a source ID
|
||||
*/
|
||||
private void sendRegistrationRequest() {
|
||||
RegistrationRequestMessage requestMessage = new RegistrationRequestMessage(ClientType.PLAYER);
|
||||
RegistrationRequestMessage requestMessage = new RegistrationRequestMessage(ClientType.PLAYER, clientId);
|
||||
|
||||
try {
|
||||
os.write(requestMessage.getBuffer());
|
||||
@@ -191,9 +195,8 @@ public class ClientToServerThread implements Runnable {
|
||||
* @param packet The registration requests packet
|
||||
*/
|
||||
private void processRegistrationResponse(StreamPacket packet){
|
||||
int sourceId = (int) Message.bytesToLong(Arrays.copyOfRange(packet.getPayload(), 0, 3));
|
||||
int sourceId = (int) Message.bytesToLong(Arrays.copyOfRange(packet.getPayload(), 0, 4));
|
||||
int statusCode = (int) Message.bytesToLong(Arrays.copyOfRange(packet.getPayload(), 4,5));
|
||||
|
||||
RegistrationResponseStatus status = RegistrationResponseStatus.getResponseStatus(statusCode);
|
||||
|
||||
if (status.equals(RegistrationResponseStatus.SUCCESS_PLAYING)){
|
||||
@@ -243,7 +246,7 @@ public class ClientToServerThread implements Runnable {
|
||||
new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendBoatActionMessage(new BoatActionMessage(BoatAction.DOWNWIND));
|
||||
sendBoatActionMessage(new BoatActionMessage(BoatAction.DOWNWIND, clientId));
|
||||
}
|
||||
}, 0, PACKET_SENDING_INTERVAL_MS
|
||||
);
|
||||
@@ -256,14 +259,14 @@ public class ClientToServerThread implements Runnable {
|
||||
new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendBoatActionMessage(new BoatActionMessage(BoatAction.UPWIND));
|
||||
sendBoatActionMessage(new BoatActionMessage(BoatAction.UPWIND, clientId));
|
||||
}
|
||||
}, 0, PACKET_SENDING_INTERVAL_MS
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sendBoatActionMessage(new BoatActionMessage(actionType));
|
||||
sendBoatActionMessage(new BoatActionMessage(actionType, clientId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -369,4 +372,20 @@ public class ClientToServerThread implements Runnable {
|
||||
public int getClientId () {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void sendXML(String path, String serverName, int legRepeats) {
|
||||
Pair<String, String> regattaRace = XMLParser.parseRaceDef(path, serverName, legRepeats);
|
||||
|
||||
sendByteBuffer(
|
||||
new XMLMessage(
|
||||
regattaRace.getKey(), XMLMessageSubType.REGATTA, regattaRace.getKey().length()
|
||||
).getBuffer()
|
||||
);
|
||||
|
||||
sendByteBuffer(
|
||||
new XMLMessage(
|
||||
regattaRace.getValue(), XMLMessageSubType.RACE, regattaRace.getValue().length()
|
||||
).getBuffer()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatStatus;
|
||||
import seng302.gameServer.messages.YachtEventType;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.model.GameKeyBind;
|
||||
import seng302.model.KeyAction;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.model.stream.parser.MarkRoundingData;
|
||||
@@ -67,6 +69,8 @@ public class GameClient {
|
||||
|
||||
private ArrayList<ClientYacht> finishedBoats = new ArrayList<>();
|
||||
|
||||
private GameKeyBind gameKeyBind; // all the key binding setting.
|
||||
|
||||
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
|
||||
|
||||
/**
|
||||
@@ -76,6 +80,7 @@ public class GameClient {
|
||||
*/
|
||||
public GameClient(Pane holder) {
|
||||
this.holderPane = holder;
|
||||
this.gameKeyBind = GameKeyBind.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,18 +123,18 @@ public class GameClient {
|
||||
* @param ipAddress IP to connect to.
|
||||
* @param portNumber Port to connect to.
|
||||
*/
|
||||
public ServerDescription runAsHost(String ipAddress, Integer portNumber, String serverName, Integer maxPlayers) {
|
||||
public ServerDescription runAsHost(String ipAddress, Integer portNumber, String serverName, Integer maxPlayers, String race, Integer numLegs) {
|
||||
XMLGenerator.setDefaultRaceName(serverName);
|
||||
GameState.setMaxPlayers(maxPlayers);
|
||||
|
||||
server = new MainServerThread();
|
||||
GameState.setMaxPlayers(maxPlayers);
|
||||
|
||||
try {
|
||||
startClientToServerThread(ipAddress, 4942);
|
||||
} catch (IOException e) {
|
||||
showConnectionError("Cannot connect to server as host");
|
||||
}
|
||||
|
||||
socketThread.sendXML(race, serverName, numLegs);
|
||||
while (regattaData == null){
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
@@ -369,16 +374,16 @@ public class GameClient {
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (e.getCode()) {
|
||||
case SPACE: // align with vmg
|
||||
socketThread.sendBoatAction(BoatAction.VMG); break;
|
||||
case PAGE_UP: // upwind
|
||||
socketThread.sendBoatAction(BoatAction.UPWIND); break;
|
||||
case PAGE_DOWN: // downwind
|
||||
socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
|
||||
case ENTER: // tack/gybe
|
||||
|
||||
if (gameKeyBind.getKeyCode(KeyAction.VMG) == e.getCode()) { // align with vmg
|
||||
socketThread.sendBoatAction(BoatAction.VMG);
|
||||
} else if (gameKeyBind.getKeyCode(KeyAction.UPWIND) == e.getCode()) { // upwind
|
||||
socketThread.sendBoatAction(BoatAction.UPWIND);
|
||||
} else if (gameKeyBind.getKeyCode(KeyAction.DOWNWIND) == e.getCode()) { // downwind
|
||||
socketThread.sendBoatAction(BoatAction.DOWNWIND);
|
||||
} else if (gameKeyBind.getKeyCode(KeyAction.TACK_GYBE) == e.getCode()) { // tack/gybe
|
||||
// if chat box is active take whatever is in there and send it to server
|
||||
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
|
||||
socketThread.sendBoatAction(BoatAction.TACK_GYBE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,15 +392,17 @@ public class GameClient {
|
||||
if (raceView.isChatInputFocused()) {
|
||||
return;
|
||||
}
|
||||
switch (e.getCode()) {
|
||||
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
|
||||
case SHIFT: // sails in/sails out
|
||||
|
||||
if (gameKeyBind.getKeyCode(KeyAction.SAILS_STATE) == e.getCode()) { // sails in/sails out
|
||||
if (allBoatsMap.get(socketThread.getClientId()).getSailIn()) {
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_OUT);
|
||||
} else {
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_IN);
|
||||
}
|
||||
allBoatsMap.get(socketThread.getClientId()).toggleSail();
|
||||
break;
|
||||
case PAGE_UP:
|
||||
case PAGE_DOWN:
|
||||
socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break;
|
||||
} else if (gameKeyBind.getKeyCode(KeyAction.UPWIND) == e.getCode()
|
||||
|| gameKeyBind.getKeyCode(KeyAction.DOWNWIND) == e.getCode()) {
|
||||
socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,4 +497,14 @@ public class GameClient {
|
||||
public Map<Integer, ClientYacht> getAllBoatsMap() {
|
||||
return allBoatsMap;
|
||||
}
|
||||
|
||||
public void sendToggleTurningModePacket() {
|
||||
if (socketThread != null) {
|
||||
if (gameKeyBind.isContinuouslyTurning()) {
|
||||
socketThread.sendBoatAction(BoatAction.CONTINUOUSLY_TURNING);
|
||||
} else {
|
||||
socketThread.sendBoatAction(BoatAction.DEFAULT_TURNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,443 +1,31 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.GeoPoint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.ScaledPoint;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||
import seng302.visualiser.fxObjects.assets_2D.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by cir27 on 20/07/17.
|
||||
* Created by cir27 on 26/09/17.
|
||||
*/
|
||||
public class GameView extends Pane {
|
||||
public abstract class GameView {
|
||||
|
||||
private double bufferSize = 50;
|
||||
private double horizontalBuffer = 0;
|
||||
double canvasWidth, canvasHeight;
|
||||
ScaledPoint scaledPoint;
|
||||
|
||||
private double canvasWidth = 1100;
|
||||
private double canvasHeight = 920;
|
||||
private boolean horizontalInversion = false;
|
||||
List<Limit> borderPoints;
|
||||
Group gameObjects = new Group();
|
||||
Group markers = new Group();
|
||||
Group tokens = new Group();
|
||||
List<CompoundMark> course = new ArrayList<>();
|
||||
List<CompoundMark> compoundMarks = new ArrayList<>();
|
||||
List<Corner> courseOrder = new ArrayList<>();
|
||||
|
||||
private double distanceScaleFactor;
|
||||
private ScaleDirection scaleDirection;
|
||||
private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint;
|
||||
private double referencePointX, referencePointY;
|
||||
|
||||
private Polygon raceBorder = new CourseBoundary();
|
||||
|
||||
/* Note that if either of these is null then values for it have not been added and the other
|
||||
should be used as the limits of the map. */
|
||||
private List<Limit> borderPoints;
|
||||
private Map<Mark, Marker2D> markerObjects;
|
||||
|
||||
private ObservableList<Node> gameObjects;
|
||||
private Group markers = new Group();
|
||||
private Group tokens = new Group();
|
||||
private List<CompoundMark> course = new ArrayList<>();
|
||||
|
||||
private ImageView mapImage = new ImageView();
|
||||
|
||||
private enum ScaleDirection {
|
||||
HORIZONTAL,
|
||||
VERTICAL
|
||||
}
|
||||
|
||||
public GameView () {
|
||||
gameObjects = this.getChildren();
|
||||
gameObjects.addAll(mapImage, raceBorder, markers, tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param newCourse the mark objects that make up the course.
|
||||
* @param sequence The sequence the marks travel through
|
||||
*/
|
||||
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
||||
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<>();
|
||||
Paint colour = Color.BLACK;
|
||||
//Creates new markers
|
||||
for (CompoundMark cMark : newCourse) {
|
||||
//Set start and end colour
|
||||
if (cMark.getId() == sequence.get(0).getCompoundMarkID()) {
|
||||
colour = Color.GREEN;
|
||||
} else if (cMark.getId() == sequence.get(sequence.size() - 1).getCompoundMarkID()) {
|
||||
colour = Color.RED;
|
||||
}
|
||||
//Create mark dots
|
||||
for (Mark mark : cMark.getMarks()) {
|
||||
makeAndBindMarker(mark, colour);
|
||||
}
|
||||
//Create gate line
|
||||
if (cMark.isGate()) {
|
||||
for (int i = 1; i < cMark.getMarks().size(); i++) {
|
||||
gates.add(
|
||||
makeAndBindGate(
|
||||
markerObjects.get(cMark.getSubMark(i)),
|
||||
markerObjects.get(cMark.getSubMark(i + 1)),
|
||||
colour
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
colour = Color.BLACK;
|
||||
}
|
||||
|
||||
createMarkArrows(sequence);
|
||||
|
||||
//Scale race to markers if there is no border.
|
||||
if (borderPoints == null) {
|
||||
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
||||
}
|
||||
//Move the Markers to initial position.
|
||||
markerObjects.forEach(((mark, marker2D) -> {
|
||||
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
||||
marker2D.setLayoutX(p2d.getX());
|
||||
marker2D.setLayoutY(p2d.getY());
|
||||
}));
|
||||
Platform.runLater(() -> {
|
||||
markers.getChildren().clear();
|
||||
markers.getChildren().addAll(gates);
|
||||
markers.getChildren().addAll(markerObjects.values());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all the data needed for to create mark arrows. Requires that a course has been
|
||||
* added to the gameview.
|
||||
* @param sequence The order in which marks are traversed.
|
||||
*/
|
||||
private void createMarkArrows (List<Corner> sequence) {
|
||||
for (int i=1; i < sequence.size()-1; i++) { //General case.
|
||||
double averageLat = 0;
|
||||
double averageLng = 0;
|
||||
int numMarks = course.get(i-1).getMarks().size();
|
||||
for (Mark mark : course.get(i-1).getMarks()) {
|
||||
averageLat += mark.getLat();
|
||||
averageLng += mark.getLng();
|
||||
}
|
||||
GeoPoint lastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||
numMarks = course.get(i+1).getMarks().size();
|
||||
averageLat = 0;
|
||||
averageLng = 0;
|
||||
for (Mark mark : course.get(i+1).getMarks()) {
|
||||
averageLat += mark.getLat();
|
||||
averageLng += mark.getLng();
|
||||
}
|
||||
GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||
// TODO: 16/08/17 This comparison doesn't need to exist but the alternative is to user server enum client side.
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
createStartLineArrows();
|
||||
createFinishLineArrows();
|
||||
}
|
||||
|
||||
private void createStartLineArrows () {
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void createFinishLineArrows () {
|
||||
double numMarks = 0;
|
||||
double averageLat = 0;
|
||||
double 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Marker and binds it's position to the given Mark.
|
||||
*
|
||||
* @param observableMark The mark to bind the marker to.
|
||||
* @param colour The desired colour of the mark
|
||||
*/
|
||||
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
||||
Marker2D marker2D = new Marker2D(colour);
|
||||
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
|
||||
markerObjects.put(observableMark, marker2D);
|
||||
observableMark.addPositionListener((mark, lat, lon) -> {
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
markerObjects.get(mark).setLayoutX(p2d.getX());
|
||||
markerObjects.get(mark).setLayoutY(p2d.getY());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new gate connecting the given marks.
|
||||
*
|
||||
* @param m1 The first Mark of the gate.
|
||||
* @param m2 The second Mark of the gate.
|
||||
* @param colour The desired colour of the gate.
|
||||
* @return the new gate.
|
||||
*/
|
||||
private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) {
|
||||
Gate gate = new Gate(colour);
|
||||
gate.startXProperty().bind(
|
||||
m1.layoutXProperty()
|
||||
);
|
||||
gate.startYProperty().bind(
|
||||
m1.layoutYProperty()
|
||||
);
|
||||
gate.endXProperty().bind(
|
||||
m2.layoutXProperty()
|
||||
);
|
||||
gate.endYProperty().bind(
|
||||
m2.layoutYProperty()
|
||||
);
|
||||
return gate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a border to the GameView and rescales to the size of the border, does not rescale if a
|
||||
* border already exists. Assumes the border is larger than the course.
|
||||
*
|
||||
* @param border the race border to be drawn.
|
||||
*/
|
||||
public void updateBorder(List<Limit> border) {
|
||||
if (borderPoints == null) {
|
||||
borderPoints = border;
|
||||
rescaleRace(new ArrayList<>(borderPoints));
|
||||
}
|
||||
|
||||
rescaleRace(new ArrayList<>(border));
|
||||
|
||||
List<Double> boundaryPoints = new ArrayList<>();
|
||||
for (Limit limit : border) {
|
||||
Point2D location = findScaledXY(limit.getLat(), limit.getLng());
|
||||
boundaryPoints.add(location.getX());
|
||||
boundaryPoints.add(location.getY());
|
||||
}
|
||||
raceBorder.getPoints().setAll(boundaryPoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescales the race to the size of the window.
|
||||
*
|
||||
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
|
||||
*/
|
||||
public void rescaleRace(List<GeoPoint> limitingCoordinates) {
|
||||
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
||||
findMinMaxPoint(limitingCoordinates);
|
||||
double minLonToMaxLon = scaleRaceExtremities();
|
||||
calculateReferencePointLocation(minLonToMaxLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with
|
||||
* the leftmost point, rightmost point, southern most point and northern most point
|
||||
* respectively.
|
||||
*/
|
||||
private void findMinMaxPoint(List<GeoPoint> points) {
|
||||
List<GeoPoint> sortedPoints = new ArrayList<>(points);
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
|
||||
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
|
||||
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng));
|
||||
minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng());
|
||||
if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) {
|
||||
horizontalInversion = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the location of a reference point, this is always the point with minimum latitude,
|
||||
* in relation to the canvas.
|
||||
*
|
||||
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
|
||||
* maximum longitude.
|
||||
*/
|
||||
private void calculateReferencePointLocation(double minLonToMaxLon) {
|
||||
GeoPoint referencePoint = minLatPoint;
|
||||
double referenceAngle;
|
||||
|
||||
if (scaleDirection == ScaleDirection.HORIZONTAL) {
|
||||
referenceAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(referencePoint, minLonPoint)
|
||||
);
|
||||
referencePointX =
|
||||
bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint));
|
||||
referencePointY = canvasHeight - (bufferSize + bufferSize);
|
||||
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
referencePointY = referencePointY / 2;
|
||||
referencePointY += bufferSize;
|
||||
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
} else {
|
||||
referencePointY = canvasHeight - bufferSize;
|
||||
referenceAngle = Math.abs(
|
||||
Math.toRadians(
|
||||
GeoUtility.getDistance(referencePoint, minLonPoint)
|
||||
)
|
||||
);
|
||||
referencePointX = bufferSize;
|
||||
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referencePointX +=
|
||||
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
|
||||
/ 2;
|
||||
referencePointX += horizontalBuffer;
|
||||
}
|
||||
if (horizontalInversion) {
|
||||
referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
|
||||
* it to distanceScaleFactor Returns the max horizontal distance of the map.
|
||||
*/
|
||||
private double scaleRaceExtremities() {
|
||||
|
||||
double vertAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
|
||||
);
|
||||
double vertDistance =
|
||||
Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint);
|
||||
double horiAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLonPoint, maxLonPoint)
|
||||
);
|
||||
if (horiAngle <= (Math.PI / 2)) {
|
||||
horiAngle = (Math.PI / 2) - horiAngle;
|
||||
} else {
|
||||
horiAngle = horiAngle - (Math.PI / 2);
|
||||
}
|
||||
double horiDistance =
|
||||
Math.cos(horiAngle) * GeoUtility.getDistance(minLonPoint, maxLonPoint);
|
||||
|
||||
double vertScale = (canvasHeight - (bufferSize + bufferSize)) / vertDistance;
|
||||
|
||||
if ((horiDistance * vertScale) > (canvasWidth - (bufferSize + bufferSize))) {
|
||||
distanceScaleFactor = (canvasWidth - (bufferSize + bufferSize)) / horiDistance;
|
||||
scaleDirection = ScaleDirection.HORIZONTAL;
|
||||
} else {
|
||||
distanceScaleFactor = vertScale;
|
||||
scaleDirection = ScaleDirection.VERTICAL;
|
||||
}
|
||||
return horiDistance;
|
||||
}
|
||||
|
||||
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
||||
double distanceFromReference;
|
||||
double angleFromReference;
|
||||
double xAxisLocation = referencePointX;
|
||||
double yAxisLocation = referencePointY;
|
||||
|
||||
angleFromReference = GeoUtility.getBearingRad(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
distanceFromReference = GeoUtility.getDistance(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||
xAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
} else if (angleFromReference >= 0) {
|
||||
angleFromReference = angleFromReference - Math.PI / 2;
|
||||
xAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||
angleFromReference = Math.abs(angleFromReference);
|
||||
xAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
} else {
|
||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||
xAxisLocation -= Math
|
||||
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||
yAxisLocation += Math
|
||||
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||
}
|
||||
if (horizontalInversion) {
|
||||
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
|
||||
}
|
||||
return new Point2D(xAxisLocation, yAxisLocation);
|
||||
}
|
||||
|
||||
|
||||
public void setSize(Double width, Double height){
|
||||
this.canvasWidth = width;
|
||||
this.canvasHeight = height;
|
||||
}
|
||||
|
||||
public void setHorizontalBuffer(Double buff){
|
||||
this.horizontalBuffer = buff;
|
||||
}
|
||||
public abstract Node getAssets();
|
||||
public abstract void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence);
|
||||
public abstract void updateBorder(List<Limit> border);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -23,8 +22,8 @@ import javafx.scene.transform.Translate;
|
||||
import org.fxyz3d.scene.Skybox;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.ScaledPoint;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
@@ -39,21 +38,24 @@ import seng302.visualiser.cameras.TopDownCamera;
|
||||
import seng302.visualiser.controllers.ViewManager;
|
||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||
import seng302.visualiser.fxObjects.assets_3D.*;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatObject;
|
||||
import seng302.visualiser.fxObjects.assets_3D.Marker3D;
|
||||
import seng302.visualiser.fxObjects.assets_3D.ModelFactory;
|
||||
import seng302.visualiser.fxObjects.assets_3D.ModelType;
|
||||
|
||||
/**
|
||||
* Collection of animated3D assets that displays a race.
|
||||
*/
|
||||
|
||||
public class GameView3D {
|
||||
public class GameView3D extends GameView{
|
||||
|
||||
private final double FOV = 60;
|
||||
private final double DEFAULT_CAMERA_DEPTH = -125;
|
||||
private final double DEFAULT_CAMERA_X = 0;
|
||||
private final double DEFAULT_CAMERA_Y = 155;
|
||||
private final double DEFAULT_CAMERA_Y = 100;
|
||||
|
||||
private Group root3D;
|
||||
private SubScene view;
|
||||
// ParallelCamera camera;
|
||||
private PerspectiveCamera camera;
|
||||
private PerspectiveCamera camera2;
|
||||
private PerspectiveCamera camera3;
|
||||
@@ -72,28 +74,20 @@ public class GameView3D {
|
||||
|
||||
/* Note that if either of these is null then values for it have not been added and the other
|
||||
should be used as the limits of the map. */
|
||||
private List<Limit> borderPoints;
|
||||
private Map<Mark, Marker3D> markerObjects;
|
||||
|
||||
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
||||
private BoatObject selectedBoat = null;
|
||||
private Group wakesGroup = new Group();
|
||||
private Group boatObjectGroup = new Group();
|
||||
private Group markers = new Group();
|
||||
private Group tokens = new Group();
|
||||
private List<CompoundMark> course = new ArrayList<>();
|
||||
private List<Node> mapTokens;
|
||||
private AnimationTimer playerBoatAnimationTimer;
|
||||
private Group trail = new Group();
|
||||
private Double windDir;
|
||||
private Skybox skybox;
|
||||
|
||||
private enum ScaleDirection {
|
||||
HORIZONTAL,
|
||||
VERTICAL
|
||||
}
|
||||
|
||||
public GameView3D () {
|
||||
canvasWidth = canvasHeight = 220;
|
||||
|
||||
camera = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH);
|
||||
camera.setFarClip(100000);
|
||||
camera.setNearClip(0.1);
|
||||
@@ -135,8 +129,10 @@ public class GameView3D {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
||||
markerObjects = new HashMap<>();
|
||||
compoundMarks = newCourse;
|
||||
|
||||
for (Corner corner : sequence) { //Makes course out of all compound marks.
|
||||
for (CompoundMark compoundMark : newCourse) {
|
||||
@@ -187,11 +183,13 @@ public class GameView3D {
|
||||
|
||||
//Scale race to markers if there is no border.
|
||||
if (borderPoints == null) {
|
||||
rescaleRace(new ArrayList<>(markerObjects.keySet()));
|
||||
scaledPoint = ScaledPoint.makeScaledPoint(
|
||||
canvasWidth, canvasHeight, new ArrayList<>(markerObjects.keySet()), true
|
||||
);
|
||||
}
|
||||
//Move the Markers to initial position.
|
||||
markerObjects.forEach(((mark, marker) -> {
|
||||
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
|
||||
Point2D p2d = scaledPoint.findScaledXY(mark.getLat(), mark.getLng());
|
||||
marker.setLayoutX(p2d.getX());
|
||||
marker.setLayoutY(p2d.getY());
|
||||
}));
|
||||
@@ -211,7 +209,7 @@ public class GameView3D {
|
||||
private void makeAndBindMarker(Mark observableMark, ModelType markerType) {
|
||||
markerObjects.put(observableMark, new Marker3D(markerType));
|
||||
observableMark.addPositionListener((mark, lat, lon) -> {
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
|
||||
markerObjects.get(mark).setLayoutX(p2d.getX());
|
||||
markerObjects.get(mark).setLayoutY(p2d.getY());
|
||||
});
|
||||
@@ -226,8 +224,8 @@ public class GameView3D {
|
||||
* @return the new gate.
|
||||
*/
|
||||
private Group makeGate(Mark m1, Mark m2, ModelType gateType) {
|
||||
Point2D m1Location = findScaledXY(m1);
|
||||
Point2D m2Location = findScaledXY(m2);
|
||||
Point2D m1Location = scaledPoint.findScaledXY(m1);
|
||||
Point2D m2Location = scaledPoint.findScaledXY(m2);
|
||||
|
||||
Group barrier = ModelFactory.importModel(gateType).getAssets();
|
||||
barrier.getTransforms().addAll(
|
||||
@@ -285,145 +283,6 @@ public class GameView3D {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with
|
||||
* the leftmost point, rightmost point, southern most point and northern most point
|
||||
* respectively.
|
||||
*/
|
||||
private void findMinMaxPoint(List<GeoPoint> points) {
|
||||
List<GeoPoint> sortedPoints = new ArrayList<>(points);
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
|
||||
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng());
|
||||
|
||||
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng));
|
||||
minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
|
||||
GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1);
|
||||
maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng());
|
||||
if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) {
|
||||
horizontalInversion = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the location of a reference point, this is always the point with minimum latitude,
|
||||
* in relation to the canvas.
|
||||
*
|
||||
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
|
||||
* maximum longitude.
|
||||
*/
|
||||
private void calculateReferencePointLocation(double minLonToMaxLon) {
|
||||
GeoPoint referencePoint = minLatPoint;
|
||||
double referenceAngle;
|
||||
|
||||
if (scaleDirection == ScaleDirection.HORIZONTAL) {
|
||||
referenceAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(referencePoint, minLonPoint)
|
||||
);
|
||||
referencePointX =
|
||||
-100 + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint));
|
||||
referencePointY = -100 + canvasHeight - (bufferSize + bufferSize);
|
||||
referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
referencePointY = referencePointY / 2;
|
||||
referencePointY += bufferSize;
|
||||
referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, maxLatPoint);
|
||||
} else {
|
||||
referencePointY = -100 + canvasHeight - bufferSize;
|
||||
referenceAngle = Math.abs(
|
||||
Math.toRadians(
|
||||
GeoUtility.getDistance(referencePoint, minLonPoint)
|
||||
)
|
||||
);
|
||||
referencePointX = -100 + bufferSize;
|
||||
referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility
|
||||
.getDistance(referencePoint, minLonPoint);
|
||||
referencePointX +=
|
||||
((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor))
|
||||
/ 2;
|
||||
}
|
||||
if (horizontalInversion) {
|
||||
referencePointX = -100 + canvasWidth - bufferSize - (referencePointX - bufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
|
||||
* it to distanceScaleFactor Returns the max horizontal distance of the map.
|
||||
*/
|
||||
private double scaleRaceExtremities() {
|
||||
|
||||
double vertAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
|
||||
);
|
||||
double vertDistance =
|
||||
Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint);
|
||||
double horiAngle = Math.abs(
|
||||
GeoUtility.getBearingRad(minLonPoint, maxLonPoint)
|
||||
);
|
||||
if (horiAngle <= (Math.PI / 2)) {
|
||||
horiAngle = (Math.PI / 2) - horiAngle;
|
||||
} else {
|
||||
horiAngle = horiAngle - (Math.PI / 2);
|
||||
}
|
||||
double horiDistance =
|
||||
Math.cos(horiAngle) * GeoUtility.getDistance(minLonPoint, maxLonPoint);
|
||||
|
||||
double vertScale = (canvasHeight - (bufferSize + bufferSize)) / vertDistance;
|
||||
|
||||
if ((horiDistance * vertScale) > (canvasWidth - (bufferSize + bufferSize))) {
|
||||
distanceScaleFactor = (canvasWidth - (bufferSize + bufferSize)) / horiDistance;
|
||||
scaleDirection = ScaleDirection.HORIZONTAL;
|
||||
} else {
|
||||
distanceScaleFactor = vertScale;
|
||||
scaleDirection = ScaleDirection.VERTICAL;
|
||||
}
|
||||
return horiDistance;
|
||||
}
|
||||
|
||||
private Point2D findScaledXY(GeoPoint unscaled) {
|
||||
return findScaledXY(unscaled.getLat(), unscaled.getLng());
|
||||
}
|
||||
|
||||
private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
|
||||
double distanceFromReference;
|
||||
double angleFromReference;
|
||||
double xAxisLocation = referencePointX;
|
||||
double yAxisLocation = referencePointY;
|
||||
|
||||
angleFromReference = GeoUtility.getBearingRad(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
distanceFromReference = GeoUtility.getDistance(
|
||||
minLatPoint, new GeoPoint(unscaledLat, unscaledLon)
|
||||
);
|
||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||
xAxisLocation += distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
yAxisLocation -= distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
} else if (angleFromReference >= 0) {
|
||||
angleFromReference = angleFromReference - Math.PI / 2;
|
||||
xAxisLocation += distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
yAxisLocation += distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||
angleFromReference = Math.abs(angleFromReference);
|
||||
xAxisLocation -= distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
yAxisLocation -= distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
} else {
|
||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||
xAxisLocation -= distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference;
|
||||
yAxisLocation += distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference;
|
||||
}
|
||||
if (horizontalInversion) {
|
||||
xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize);
|
||||
}
|
||||
return new Point2D(xAxisLocation, yAxisLocation);
|
||||
}
|
||||
|
||||
public void cameraMovement(KeyEvent event) {
|
||||
switch (event.getCode()) {
|
||||
case NUMPAD8:
|
||||
@@ -470,19 +329,6 @@ public class GameView3D {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescales the race to the size of the window.
|
||||
*
|
||||
* @param limitingCoordinates the set of geo points that contains the extremities of the race.
|
||||
*/
|
||||
private void rescaleRace(List<GeoPoint> limitingCoordinates) {
|
||||
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
||||
findMinMaxPoint(limitingCoordinates);
|
||||
double minLonToMaxLon = scaleRaceExtremities();
|
||||
calculateReferencePointLocation(minLonToMaxLon);
|
||||
// drawGoogleMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all the boats.
|
||||
* @param yachts The yachts to set in the race
|
||||
@@ -500,7 +346,7 @@ public class GameView3D {
|
||||
boatObjectGroup.getChildren().add(newBoat);
|
||||
clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
|
||||
BoatObject bo = boatObjects.get(boat);
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
||||
});
|
||||
|
||||
@@ -529,18 +375,20 @@ public class GameView3D {
|
||||
public void updateBorder(List<Limit> border) {
|
||||
if (borderPoints == null) {
|
||||
borderPoints = border;
|
||||
rescaleRace(new ArrayList<>(borderPoints));
|
||||
scaledPoint = ScaledPoint.makeScaledPoint(
|
||||
canvasWidth, canvasHeight, new ArrayList<>(borderPoints), true
|
||||
);
|
||||
}
|
||||
List<Node> boundaryAssets = new ArrayList<>();
|
||||
|
||||
Point2D lastLocation = findScaledXY(border.get(0).getLat(), border.get(0).getLng());
|
||||
Point2D lastLocation = scaledPoint.findScaledXY(border.get(0).getLat(), border.get(0).getLng());
|
||||
Group pylon = ModelFactory.importModel(ModelType.BORDER_PYLON).getAssets();
|
||||
pylon.setLayoutX(lastLocation.getX());
|
||||
pylon.setLayoutY(lastLocation.getY());
|
||||
boundaryAssets.add(pylon);
|
||||
|
||||
for (int i=1; i<border.size(); i++) {
|
||||
Point2D location = findScaledXY(border.get(i).getLat(), border.get(i).getLng());
|
||||
Point2D location = scaledPoint.findScaledXY(border.get(i).getLat(), border.get(i).getLng());
|
||||
pylon = ModelFactory.importModel(ModelType.BORDER_PYLON).getAssets();
|
||||
pylon.setLayoutX(location.getX());
|
||||
pylon.setLayoutY(location.getY());
|
||||
@@ -566,7 +414,7 @@ public class GameView3D {
|
||||
boundaryAssets.add(pylon);
|
||||
}
|
||||
|
||||
Point2D firstLocation = findScaledXY(border.get(0).getLat(), border.get(0).getLng());
|
||||
Point2D firstLocation = scaledPoint.findScaledXY(border.get(0).getLat(), border.get(0).getLng());
|
||||
Group barrier = ModelFactory.importModel(ModelType.BORDER_BARRIER).getAssets();
|
||||
barrier.getTransforms().addAll(
|
||||
new Rotate(
|
||||
@@ -630,19 +478,17 @@ public class GameView3D {
|
||||
playerYacht.toggleSail();
|
||||
playerBoatAnimationTimer = new AnimationTimer() {
|
||||
|
||||
double count = 60;
|
||||
Point2D lastLocation = findScaledXY(playerYacht.getLocation());
|
||||
Point2D lastLocation = scaledPoint.findScaledXY(playerYacht.getLocation());
|
||||
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
if (--count == 0) {
|
||||
count = 60;
|
||||
Point2D location = scaledPoint.findScaledXY(playerYacht.getLocation());
|
||||
if (Math.abs(lastLocation.distance(location)) > 2) {
|
||||
Node segment = ModelFactory.importModel(ModelType.TRAIL_SEGMENT).getAssets();
|
||||
Point2D location = findScaledXY(playerYacht.getLocation());
|
||||
location = scaledPoint.findScaledXY(playerYacht.getLocation());
|
||||
segment.getTransforms().addAll(
|
||||
new Translate(location.getX(), location.getY(), 0),
|
||||
new Rotate(playerYacht.getHeading(), new Point3D(0,0,1)),
|
||||
new Scale(1, lastLocation.distance(location) / 5, 1)
|
||||
new Rotate(playerYacht.getHeading(), new Point3D(0,0,1))
|
||||
);
|
||||
trail.getChildren().add(segment);
|
||||
if (trail.getChildren().size() > 50) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.scene.Node;
|
||||
import javafx.util.Pair;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
/**
|
||||
* Makes maps from map definition xml files.
|
||||
*/
|
||||
public class MapMaker {
|
||||
|
||||
private List<MapPreview> mapPreviews = new ArrayList<>();
|
||||
private List<RaceXMLData> races = new ArrayList<>();
|
||||
private List<RegattaXMLData> regattas = new ArrayList<>();
|
||||
private List<String> filePaths = new ArrayList<>();
|
||||
private static MapMaker instance;
|
||||
private int index = 0;
|
||||
|
||||
public static MapMaker getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new MapMaker();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private MapMaker() {
|
||||
File dir = new File(MapMaker.class.getResource("/maps/").getPath());
|
||||
File[] directoryListing = dir.listFiles();
|
||||
if (directoryListing != null) {
|
||||
for (File child : directoryListing) {
|
||||
Pair<String, String> regattaRace = XMLParser.parseRaceDef(
|
||||
child.getAbsolutePath(), "", 1
|
||||
);
|
||||
filePaths.add(child.getAbsolutePath());
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db;
|
||||
Document doc = null;
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(new InputSource(new StringReader(regattaRace.getKey())));
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
regattas.add(XMLParser.parseRegatta(doc));
|
||||
try {
|
||||
db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(new InputSource(new StringReader(regattaRace.getValue())));
|
||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
RaceXMLData race = XMLParser.parseRace(doc);
|
||||
MapPreview mapPreview = new MapPreview(
|
||||
new ArrayList<>(race.getCompoundMarks().values()),
|
||||
race.getMarkSequence(), race.getCourseLimit()
|
||||
);
|
||||
mapPreviews.add(mapPreview);
|
||||
races.add(race);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void next() {
|
||||
index += 1;
|
||||
if (index >= mapPreviews.size()) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void previous() {
|
||||
index -= 1;
|
||||
if (index < 0) {
|
||||
index = mapPreviews.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public Node getCurrentGameView() {
|
||||
return mapPreviews.get(index).getAssets();
|
||||
}
|
||||
|
||||
public RaceXMLData getCurrentRace() {
|
||||
return races.get(index);
|
||||
}
|
||||
|
||||
public RegattaXMLData getCurrentRegatta() {
|
||||
return regattas.get(index);
|
||||
}
|
||||
|
||||
public String getCurrentRacePath() {
|
||||
return filePaths.get(index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.Polygon;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.ScaledPoint;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||
import seng302.visualiser.fxObjects.assets_2D.CourseBoundary;
|
||||
import seng302.visualiser.fxObjects.assets_2D.Gate;
|
||||
import seng302.visualiser.fxObjects.assets_2D.Marker2D;
|
||||
|
||||
/**
|
||||
* Created by cir27 on 20/07/17.
|
||||
*/
|
||||
public class MapPreview extends GameView {
|
||||
|
||||
private Polygon raceBorder = new CourseBoundary();
|
||||
private Map<Mark, Marker2D> markerObjects;
|
||||
|
||||
public MapPreview(List<CompoundMark> marks, List<Corner> course, List<Limit> border) {
|
||||
this.compoundMarks = marks;
|
||||
this.courseOrder = course;
|
||||
this.borderPoints = border;
|
||||
gameObjects.getChildren().addAll(raceBorder, markers, tokens);
|
||||
gameObjects.parentProperty().addListener((obs, old, parent) -> {
|
||||
if (parent != null) {
|
||||
canvasWidth = parent.prefWidth(1);
|
||||
canvasHeight = parent.prefHeight(1);
|
||||
updateBorder(borderPoints);
|
||||
updateCourse(compoundMarks, courseOrder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getAssets() {
|
||||
return gameObjects;
|
||||
}
|
||||
|
||||
public void setSize(double width, double height) {
|
||||
canvasHeight = height;
|
||||
canvasWidth = width;
|
||||
updateBorder(borderPoints);
|
||||
updateCourse(compoundMarks, courseOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param newCourse the mark objects that make up the course.
|
||||
* @param sequence The sequence the marks travel through
|
||||
*/
|
||||
@Override
|
||||
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
|
||||
|
||||
if (newCourse.size() == 0) {
|
||||
return;
|
||||
}
|
||||
compoundMarks = newCourse;
|
||||
markerObjects = new HashMap<>();
|
||||
courseOrder = sequence;
|
||||
|
||||
for (Corner corner : courseOrder) { //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<>();
|
||||
Paint colour = Color.BLACK;
|
||||
//Creates new markers
|
||||
for (CompoundMark cMark : newCourse) {
|
||||
//Set start and end colour
|
||||
if (cMark.getId() == sequence.get(0).getCompoundMarkID()) {
|
||||
colour = Color.GREEN;
|
||||
} else if (cMark.getId() == sequence.get(sequence.size() - 1).getCompoundMarkID()) {
|
||||
colour = Color.RED;
|
||||
}
|
||||
//Create mark dots
|
||||
for (Mark mark : cMark.getMarks()) {
|
||||
makeAndBindMarker(mark, colour);
|
||||
}
|
||||
//Create gate line
|
||||
if (cMark.isGate()) {
|
||||
for (int i = 1; i < cMark.getMarks().size(); i++) {
|
||||
gates.add(
|
||||
makeAndBindGate(
|
||||
markerObjects.get(cMark.getSubMark(i)),
|
||||
markerObjects.get(cMark.getSubMark(i + 1)),
|
||||
colour
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
colour = Color.BLACK;
|
||||
}
|
||||
|
||||
createMarkArrows(sequence);
|
||||
|
||||
//Scale race to markers if there is no border.
|
||||
if (borderPoints == null) {
|
||||
scaledPoint = ScaledPoint.makeScaledPoint(
|
||||
canvasWidth, canvasHeight, new ArrayList<>(markerObjects.keySet()), false
|
||||
);
|
||||
}
|
||||
//Move the Markers to initial position.
|
||||
markerObjects.forEach(((mark, marker2D) -> {
|
||||
Point2D p2d = scaledPoint.findScaledXY(mark.getLat(), mark.getLng());
|
||||
marker2D.setLayoutX(p2d.getX());
|
||||
marker2D.setLayoutY(p2d.getY());
|
||||
}));
|
||||
Platform.runLater(() -> {
|
||||
markers.getChildren().clear();
|
||||
markers.getChildren().addAll(gates);
|
||||
markers.getChildren().addAll(markerObjects.values());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all the data needed for to create mark arrows. Requires that a course has been
|
||||
* added to the gameview.
|
||||
* @param sequence The order in which marks are traversed.
|
||||
*/
|
||||
private void createMarkArrows (List<Corner> sequence) {
|
||||
for (int i=1; i < sequence.size()-1; i++) { //General case.
|
||||
double averageLat = 0;
|
||||
double averageLng = 0;
|
||||
int numMarks = course.get(i-1).getMarks().size();
|
||||
for (Mark mark : course.get(i-1).getMarks()) {
|
||||
averageLat += mark.getLat();
|
||||
averageLng += mark.getLng();
|
||||
}
|
||||
GeoPoint lastMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||
numMarks = course.get(i+1).getMarks().size();
|
||||
averageLat = 0;
|
||||
averageLng = 0;
|
||||
for (Mark mark : course.get(i+1).getMarks()) {
|
||||
averageLat += mark.getLat();
|
||||
averageLng += mark.getLng();
|
||||
}
|
||||
GeoPoint nextMarkAv = new GeoPoint(averageLat / numMarks, averageLng / numMarks);
|
||||
// TODO: 16/08/17 This comparison doesn't need to exist but the alternative is to user server enum client side.
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
createStartLineArrows();
|
||||
createFinishLineArrows();
|
||||
}
|
||||
|
||||
private void createStartLineArrows () {
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void createFinishLineArrows () {
|
||||
double numMarks = 0;
|
||||
double averageLat = 0;
|
||||
double 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Marker and binds it's position to the given Mark.
|
||||
*
|
||||
* @param observableMark The mark to bind the marker to.
|
||||
* @param colour The desired colour of the mark
|
||||
*/
|
||||
private void makeAndBindMarker(Mark observableMark, Paint colour) {
|
||||
Marker2D marker2D = new Marker2D(colour);
|
||||
// marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90));
|
||||
markerObjects.put(observableMark, marker2D);
|
||||
observableMark.addPositionListener((mark, lat, lon) -> {
|
||||
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
|
||||
markerObjects.get(mark).setLayoutX(p2d.getX());
|
||||
markerObjects.get(mark).setLayoutY(p2d.getY());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new gate connecting the given marks.
|
||||
*
|
||||
* @param m1 The first Mark of the gate.
|
||||
* @param m2 The second Mark of the gate.
|
||||
* @param colour The desired colour of the gate.
|
||||
* @return the new gate.
|
||||
*/
|
||||
private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) {
|
||||
Gate gate = new Gate(colour);
|
||||
gate.startXProperty().bind(
|
||||
m1.layoutXProperty()
|
||||
);
|
||||
gate.startYProperty().bind(
|
||||
m1.layoutYProperty()
|
||||
);
|
||||
gate.endXProperty().bind(
|
||||
m2.layoutXProperty()
|
||||
);
|
||||
gate.endYProperty().bind(
|
||||
m2.layoutYProperty()
|
||||
);
|
||||
return gate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a border to the GameView and rescales to the size of the border, does not rescale if a
|
||||
* border already exists. Assumes the border is larger than the course.
|
||||
*
|
||||
* @param border the race border to be drawn.
|
||||
*/
|
||||
@Override
|
||||
public void updateBorder(List<Limit> border) {
|
||||
if (border.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
borderPoints = border;
|
||||
scaledPoint = ScaledPoint.makeScaledPoint(canvasWidth, canvasHeight, border, false);
|
||||
|
||||
List<Double> boundaryPoints = new ArrayList<>();
|
||||
for (Limit limit : border) {
|
||||
Point2D location = scaledPoint.findScaledXY(limit.getLat(), limit.getLng());
|
||||
boundaryPoints.add(location.getX());
|
||||
boundaryPoints.add(location.getY());
|
||||
}
|
||||
raceBorder.getPoints().setAll(boundaryPoints);
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,6 @@ public class FinishScreenViewController implements Initializable {
|
||||
|
||||
public void switchToStartScreenView() {
|
||||
Sounds.playButtonClick();
|
||||
//TODO merge fix
|
||||
setContentPane("/views/StartScreenView.fxml");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@ package seng302.visualiser.controllers;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -9,7 +15,7 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
@@ -24,21 +30,16 @@ import seng302.model.mark.Corner;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.discoveryServer.DiscoveryServerClient;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameView;
|
||||
import seng302.visualiser.MapPreview;
|
||||
import seng302.visualiser.controllers.cells.PlayerCell;
|
||||
import seng302.visualiser.controllers.dialogs.BoatCustomizeController;
|
||||
import seng302.visualiser.controllers.dialogs.DirectConnectController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
public class LobbyController implements Initializable {
|
||||
|
||||
private final double INITIAL_MAP_HEIGHT = 770d;
|
||||
private final double INITIAL_MAP_WIDTH = 574d;
|
||||
|
||||
//--------FXML BEGIN--------//
|
||||
@FXML
|
||||
private VBox playerListVBox;
|
||||
@@ -53,7 +54,7 @@ public class LobbyController implements Initializable {
|
||||
@FXML
|
||||
private Label mapName;
|
||||
@FXML
|
||||
private Pane serverMap;
|
||||
private AnchorPane serverMap;
|
||||
@FXML
|
||||
private Label roomLabel;
|
||||
//---------FXML END---------//
|
||||
@@ -62,8 +63,8 @@ public class LobbyController implements Initializable {
|
||||
private JFXDialog customizationDialog;
|
||||
public Color playersColor;
|
||||
private Map<Integer, ClientYacht> playerBoats;
|
||||
private Double mapWidth, mapHeight;
|
||||
private GameView gameView;
|
||||
private Double mapWidth = INITIAL_MAP_WIDTH, mapHeight = INITIAL_MAP_HEIGHT;
|
||||
private MapPreview mapPreview;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
@@ -99,16 +100,16 @@ public class LobbyController implements Initializable {
|
||||
ViewManager.getInstance().getPlayerList().setAll(ViewManager.getInstance().getPlayerList().sorted());
|
||||
});
|
||||
|
||||
customizeButton.setOnMouseReleased(event -> {
|
||||
customizationDialog = createCustomizeDialog();
|
||||
Sounds.playButtonClick();
|
||||
customizationDialog.show();
|
||||
});
|
||||
|
||||
Platform.runLater(() -> {
|
||||
Integer playerId = ViewManager.getInstance().getGameClient().getServerThread().getClientId();
|
||||
|
||||
playersColor = Colors.getColor(playerId - 1);
|
||||
customizationDialog = createCustomizeDialog();
|
||||
|
||||
customizeButton.setOnMouseReleased(event -> {
|
||||
Sounds.playButtonClick();
|
||||
customizationDialog.show();
|
||||
});
|
||||
});
|
||||
|
||||
leaveLobbyButton.setOnMouseEntered(e -> Sounds.playHoverSound());
|
||||
@@ -145,44 +146,29 @@ public class LobbyController implements Initializable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void refreshMapView(){
|
||||
RaceXMLData raceData = ViewManager.getInstance().getGameClient().getCourseData();
|
||||
List<Limit> border = raceData.getCourseLimit();
|
||||
List<CompoundMark> marks = new ArrayList<CompoundMark>(raceData.getCompoundMarks().values());
|
||||
List<Corner> corners = raceData.getMarkSequence();
|
||||
|
||||
gameView.setSize(mapWidth, mapHeight);
|
||||
|
||||
// Update game view
|
||||
gameView.updateBorder(border);
|
||||
gameView.updateCourse(marks, corners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a top down preview of the race course map.
|
||||
*/
|
||||
private void initMapPreview() {
|
||||
gameView = new GameView();
|
||||
gameView.setHorizontalBuffer(330d);
|
||||
RaceXMLData raceData = ViewManager.getInstance().getGameClient().getCourseData();
|
||||
List<Limit> border = raceData.getCourseLimit();
|
||||
List<CompoundMark> marks = new ArrayList<>(raceData.getCompoundMarks().values());
|
||||
List<Corner> corners = raceData.getMarkSequence();
|
||||
|
||||
mapWidth = 770d;
|
||||
mapHeight = 574d;
|
||||
|
||||
// Add game view
|
||||
mapPreview = new MapPreview(marks, corners, border);
|
||||
serverMap.getChildren().clear();
|
||||
serverMap.getChildren().add(gameView);
|
||||
serverMap.getChildren().add(mapPreview.getAssets());
|
||||
|
||||
mapPreview.setSize(mapWidth, mapHeight);
|
||||
|
||||
serverMap.widthProperty().addListener((observable, oldValue, newValue) -> {
|
||||
mapWidth = newValue.doubleValue();
|
||||
refreshMapView();
|
||||
mapPreview.setSize(mapWidth, mapHeight);
|
||||
});
|
||||
|
||||
//
|
||||
serverMap.heightProperty().addListener((observable, oldValue, newValue) -> {
|
||||
mapHeight = newValue.doubleValue();
|
||||
refreshMapView();
|
||||
mapPreview.setSize(mapWidth, mapHeight);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ import java.util.concurrent.TimeUnit;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -26,8 +24,6 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.SubScene;
|
||||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.chart.XYChart.Data;
|
||||
import javafx.scene.chart.XYChart.Series;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
@@ -95,7 +91,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
@FXML
|
||||
private Label timerLabel;
|
||||
@FXML
|
||||
private StackPane contentAnchorPane;
|
||||
private StackPane contentStackPane;
|
||||
|
||||
private GridPane contentGridPane;
|
||||
@FXML
|
||||
@@ -144,34 +140,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
Sounds.stopMusic();
|
||||
Sounds.playRaceMusic();
|
||||
|
||||
finishScreenDialog = createFinishDialog();
|
||||
|
||||
// Load a default important annotation state
|
||||
//importantAnnotations = new ImportantAnnotationsState();
|
||||
|
||||
//Formatting the y axis of the sparkline
|
||||
// raceSparkLine.getYAxis().setRotate(180);
|
||||
// raceSparkLine.getYAxis().setTickLabelRotation(180);
|
||||
// raceSparkLine.getYAxis().setTranslateX(-5);
|
||||
//raceSparkLine.visibleProperty().setValue(false);
|
||||
//raceSparkLine.getYAxis().setAutoRanging(false);
|
||||
//sparklineYAxis.setTickMarkVisible(false);
|
||||
|
||||
//positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
// raceSparkLine.visibleProperty().setValue(false);
|
||||
// raceSparkLine.getYAxis().setAutoRanging(false);
|
||||
// sparklineYAxis.setTickMarkVisible(false);
|
||||
// positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
|
||||
//selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||
// rvAnchorPane.prefWidthProperty().bind(ViewManager.getInstance().getDecorator().widthProperty());
|
||||
// rvAnchorPane.prefHeightProperty().bind(ViewManager.getInstance().getDecorator().heightProperty());
|
||||
// selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||
// windArrowHolder.getChildren().addAll(windArrow);
|
||||
// windArrow.setLayoutX(windArrowHolder.getWidth() / 2);
|
||||
// windArrow.setLayoutY(windArrowHolder.getHeight() / 2);
|
||||
|
||||
// selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||
chatInput.lengthProperty().addListener((obs, oldLen, newLen) -> {
|
||||
if (newLen.intValue() > CHAT_LIMIT) {
|
||||
chatInput.setText(chatInput.getText().substring(0, CHAT_LIMIT));
|
||||
@@ -185,51 +153,44 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
chatHistory.prefHeightProperty().bind(
|
||||
chatHistoryHolder.heightProperty()
|
||||
);
|
||||
// chatHistory.setFitToWidth(true);
|
||||
// chatHistory.setFitToHeight(true);
|
||||
// chatHistory.textProperty().addListener((obs, oldValue, newValue) -> {
|
||||
// chatHistory.setScrollTop(Double.MAX_VALUE);
|
||||
// });
|
||||
rvAnchorPane.setOnMouseClicked((event) ->
|
||||
rvAnchorPane.requestFocus()
|
||||
);
|
||||
|
||||
contentStackPane.setOnMouseClicked(event -> {
|
||||
contentStackPane.requestFocus();
|
||||
});
|
||||
Platform.runLater(contentStackPane::requestFocus);
|
||||
//Makes the chat history non transparent when clicked on
|
||||
chatInput.focusedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
|
||||
Boolean newValue) {
|
||||
chatInput.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
chatHistory.increaseOpacity();
|
||||
} else {
|
||||
chatHistory.decreaseOpacity();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showFinishDialog(ArrayList<ClientYacht> finishedBoats) {
|
||||
raceState.setRaceStarted(false);
|
||||
finishDialogController.setFinishedBoats(finishedBoats);
|
||||
finishScreenDialog.show();
|
||||
createFinishDialog(finishedBoats);
|
||||
}
|
||||
|
||||
private JFXDialog createFinishDialog() {
|
||||
/**
|
||||
* Create finishScreenDialog and set up finishDialogController.
|
||||
*/
|
||||
private void createFinishDialog(ArrayList<ClientYacht> finishedBoats) {
|
||||
FXMLLoader dialog = new FXMLLoader(
|
||||
getClass().getResource("/views/dialogs/RaceFinishDialog.fxml"));
|
||||
|
||||
JFXDialog finishScreenDialog = null;
|
||||
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
finishScreenDialog = new JFXDialog(contentAnchorPane, dialog.load(),
|
||||
finishScreenDialog = new JFXDialog(contentStackPane, dialog.load(),
|
||||
JFXDialog.DialogTransition.CENTER);
|
||||
finishDialogController = dialog.getController();
|
||||
finishDialogController.setFinishedBoats(finishedBoats);
|
||||
finishScreenDialog.show();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
finishDialogController = dialog.getController();
|
||||
|
||||
return finishScreenDialog;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +208,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
while (c.next()) {
|
||||
if (c.wasPermutated()) {
|
||||
updateOrder(raceState.getPlayerPositions());
|
||||
updateSparkLine();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -256,9 +216,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
|
||||
updateOrder(raceState.getPlayerPositions());
|
||||
gameView = new GameView3D();
|
||||
// gameView.setFrameRateFXText(fpsDisplay);
|
||||
Platform.runLater(() -> {
|
||||
contentAnchorPane.getChildren().add(0, gameView.getAssets());
|
||||
contentStackPane.getChildren().add(0, gameView.getAssets());
|
||||
((SubScene) gameView.getAssets()).widthProperty()
|
||||
.bind(ViewManager.getInstance().getStage().widthProperty());
|
||||
((SubScene) gameView.getAssets()).heightProperty()
|
||||
@@ -270,11 +229,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
gameView.updateCourse(
|
||||
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
|
||||
);
|
||||
// gameView.enableZoom();
|
||||
gameView.setBoatAsPlayer(player);
|
||||
// gameView.startRace();
|
||||
|
||||
// raceState.addCollisionListener(gameView::drawCollision);
|
||||
|
||||
raceState.windDirectionProperty().addListener((obs, oldDirection, newDirection) -> {
|
||||
gameView.setWindDir(newDirection.doubleValue());
|
||||
Platform.runLater(() -> updateWindDirection(newDirection.doubleValue()));
|
||||
@@ -287,9 +245,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
updateWindSpeed(raceState.getWindSpeed());
|
||||
});
|
||||
gameView.setWindDir(raceState.windDirectionProperty().doubleValue());
|
||||
Platform.runLater(() -> {
|
||||
initializeUpdateTimer();
|
||||
});
|
||||
Platform.runLater(this::initializeUpdateTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,137 +342,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseFPSCheckBox() {
|
||||
// toggleFps.selectedProperty().addListener((obs, oldVal, newVal) ->
|
||||
// gameView.setFPSVisibility(toggleFps.isSelected())
|
||||
// );
|
||||
}
|
||||
|
||||
private void initialiseAnnotationSlider() {
|
||||
// annotationSlider.setLabelFormatter(new StringConverter<Double>() {
|
||||
// @Override
|
||||
// public String toString(Double n) {
|
||||
// if (n == 0) {
|
||||
// return "None";
|
||||
// }
|
||||
// if (n == 1) {
|
||||
// return "Important";
|
||||
// }
|
||||
// if (n == 2) {
|
||||
// return "All";
|
||||
// }
|
||||
// return "All";
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Double fromString(String s) {
|
||||
// switch (s) {
|
||||
// case "None":
|
||||
// return 0d;
|
||||
// case "Important":
|
||||
// return 1d;
|
||||
// case "All":
|
||||
// return 2d;
|
||||
//
|
||||
// default:
|
||||
// return 2d;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// annotationSlider.setValue(2);
|
||||
// annotationSlider.valueProperty().addListener((obs, oldVal, newVal) ->
|
||||
// setAnnotations((int) annotationSlider.getValue())
|
||||
// );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to add any new yachts into the race that may have started late or not have had data received yet
|
||||
*/
|
||||
private void updateSparkLine(){
|
||||
// // TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed.
|
||||
// // Collect the racing yachts that aren't already in the chart
|
||||
// sparkLineData.clear();
|
||||
// List<ClientYacht> sparkLineCandidates = new ArrayList<>(participants.values());
|
||||
// // Create a new data series for new yachts
|
||||
// sparkLineCandidates
|
||||
// .stream()
|
||||
// .filter(yacht -> yacht.getPosition() != null)
|
||||
// .forEach(yacht -> {
|
||||
// Series<String, Double> yachtData = new Series<>();
|
||||
// yachtData.setName(yacht.getSourceId().toString());
|
||||
// yachtData.getData().add(
|
||||
// new Data<>(
|
||||
// Integer.toString(yacht.getLegNumber()),
|
||||
// 1.0 + participants.size() - yacht.getPosition()
|
||||
// )
|
||||
// );
|
||||
// sparkLineData.add(yachtData);
|
||||
// });
|
||||
//
|
||||
// // Lambda function to sort the series in order of leg (later legs shown more to the right)
|
||||
// sparkLineData.sort((o1, o2) -> {
|
||||
// Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
|
||||
// Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
|
||||
// if (leg2 < leg1){
|
||||
// return 1;
|
||||
// } else {
|
||||
// return -1;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// // Adds the new data series to the sparkline (and set the colour of the series)
|
||||
// Platform.runLater(() -> {
|
||||
// sparkLineData
|
||||
// .stream()
|
||||
// .filter(spark -> !raceSparkLine.getData().contains(spark))
|
||||
// .forEach(spark -> {
|
||||
// raceSparkLine.getData().add(spark);
|
||||
// spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
private void initialiseSparkLine() {
|
||||
// sparklineYAxis.setUpperBound(participants.size() + 1);
|
||||
// raceSparkLine.setCreateSymbols(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the yachts sparkline of the desired yacht and using the new leg number
|
||||
* @param yacht The yacht to be updated on the sparkline
|
||||
* @param legNumber the leg number that the position will be assigned to
|
||||
*/
|
||||
void updateYachtPositionSparkline(ClientYacht yacht, Integer legNumber){
|
||||
for (XYChart.Series<String, Double> positionData : sparkLineData) {
|
||||
positionData.getData().add(
|
||||
new Data<>(
|
||||
Integer.toString(legNumber),
|
||||
1.0 + participants.size() - yacht.getPlacing()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the colour as an rgb string
|
||||
*/
|
||||
private String getBoatColorAsRGB(String yachtId){
|
||||
Color color = participants.get(Integer.valueOf(yachtId)).getColour();
|
||||
if (color == null){
|
||||
return String.format("#%02X%02X%02X",255,255,255);
|
||||
}
|
||||
return String.format( "#%02X%02X%02X",
|
||||
(int)( color.getRed() * 255 ),
|
||||
(int)( color.getGreen() * 255 ),
|
||||
(int)( color.getBlue() * 255 )
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -874,7 +699,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
public String readChatInput() {
|
||||
String chat = chatInput.getText();
|
||||
chatInput.clear();
|
||||
rvAnchorPane.requestFocus();
|
||||
contentStackPane.requestFocus();
|
||||
return chat;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,17 +139,25 @@ public class ServerListController implements Initializable, ServerListenerDelega
|
||||
});
|
||||
|
||||
// Set up dialog for server creation
|
||||
serverListHostButton.setOnAction(action -> {
|
||||
showServerCreationDialog();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows Server Creation Dialog when "Host" button is clicked.
|
||||
*/
|
||||
private void showServerCreationDialog() {
|
||||
Platform.runLater(() -> {
|
||||
FXMLLoader dialogContent = new FXMLLoader(getClass().getResource(
|
||||
"/views/dialogs/ServerCreationDialog.fxml"));
|
||||
try {
|
||||
JFXDialog dialog = new JFXDialog(serverListMainStackPane, dialogContent.load(),
|
||||
DialogTransition.CENTER);
|
||||
serverListHostButton.setOnAction(action -> {
|
||||
dialog.show();
|
||||
Sounds.playButtonClick();
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.warn("Could not create Server Creation Dialog.");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package seng302.visualiser.controllers;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDecorator;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import com.jfoenix.controls.JFXDialog.DialogTransition;
|
||||
import com.jfoenix.controls.JFXSnackbar;
|
||||
import com.jfoenix.svg.SVGGlyph;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
@@ -15,6 +18,7 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.SceneAntialiasing;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
@@ -23,6 +27,7 @@ import seng302.gameServer.ServerAdvertiser;
|
||||
import seng302.utilities.BonjourInstallChecker;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameClient;
|
||||
import seng302.visualiser.controllers.dialogs.KeyBindingDialogController;
|
||||
|
||||
public class ViewManager {
|
||||
|
||||
@@ -32,12 +37,9 @@ public class ViewManager {
|
||||
private HashMap<String, String> properties; //TODO is this the best way to do this??
|
||||
private ObservableList<String> playerList;
|
||||
private Logger logger = LoggerFactory.getLogger(ViewManager.class);
|
||||
|
||||
public Stage getStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
private Stage stage;
|
||||
private JFXSnackbar jfxSnackbar;
|
||||
private JFXDialog keyBindingDialog;
|
||||
|
||||
private ViewManager() {
|
||||
properties = new HashMap<>();
|
||||
@@ -99,6 +101,8 @@ public class ViewManager {
|
||||
gameClient.stopGame();
|
||||
System.exit(0);
|
||||
});
|
||||
|
||||
jfxSnackbar = new JFXSnackbar(decorator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,12 +123,31 @@ public class ViewManager {
|
||||
//Get the button box
|
||||
HBox btns = (HBox) decorator.getChildren().get(0);
|
||||
|
||||
//Create settings button -- [WIP]
|
||||
JFXButton btnKeyBinding = new JFXButton();
|
||||
btnKeyBinding.setText(" Key Bindings");
|
||||
btnKeyBinding.setStyle("-fx-text-fill:#fff");
|
||||
btnKeyBinding.getStyleClass().add("jfx-decorator-button");
|
||||
btnKeyBinding.setCursor(Cursor.HAND);
|
||||
btnKeyBinding.setFocusTraversable(false);
|
||||
|
||||
btnKeyBinding.setOnMouseClicked(event -> Platform.runLater(() -> {
|
||||
try {
|
||||
if (!checkDialogOpened(decorator.getChildren())) {
|
||||
showKeyBindingDialog();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("Something went wrong when opening key bind dialog");
|
||||
}
|
||||
}));
|
||||
|
||||
//Create new button
|
||||
JFXButton btnMute = new JFXButton();
|
||||
btnMute.setText(" Toggle Sound");
|
||||
btnMute.setStyle("-fx-text-fill:#fff");
|
||||
btnMute.getStyleClass().add("jfx-decorator-button");
|
||||
btnMute.setCursor(Cursor.HAND);
|
||||
btnMute.setFocusTraversable(false);
|
||||
|
||||
//Create Graphics
|
||||
SVGGlyph spacer = new SVGGlyph(0, "SPACER", "", Color.WHITE);
|
||||
@@ -134,9 +157,13 @@ public class ViewManager {
|
||||
SVGGlyph volumeOff = new SVGGlyph(0, "VOLUME_ON",
|
||||
"M13.5,9 C13.5,7.2 12.5,5.7 11,5 L11,7.2 L13.5,9.7 L13.5,9 L13.5,9 Z M16,9 C16,9.9 15.8,10.8 15.5,11.6 L17,13.1 C17.7,11.9 18,10.4 18,8.9 C18,4.6 15,1 11,0.1 L11,2.2 C13.9,3.2 16,5.8 16,9 L16,9 Z M1.3,0 L0,1.3 L4.7,6 L0,6 L0,12 L4,12 L9,17 L9,10.3 L13.3,14.6 C12.6,15.1 11.9,15.5 11,15.8 L11,17.9 C12.4,17.6 13.6,17 14.7,16.1 L16.7,18.1 L18,16.8 L9,7.8 L1.3,0 L1.3,0 Z M9,1 L6.9,3.1 L9,5.2 L9,1 L9,1 Z",
|
||||
Color.WHITE);
|
||||
SVGGlyph keyBindingGlyph = new SVGGlyph(0, "KEY_BINDING",
|
||||
"M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z",
|
||||
Color.WHITE);
|
||||
volumeOn.setSize(16, 16);
|
||||
volumeOff.setSize(16, 16);
|
||||
spacer.setSize(40, 16);
|
||||
keyBindingGlyph.setSize(24, 16);
|
||||
|
||||
// Determine which graphic should go on the button
|
||||
if (Sounds.isMusicMuted() && Sounds.isSoundEffectsMuted()) {
|
||||
@@ -145,9 +172,12 @@ public class ViewManager {
|
||||
btnMute.setGraphic(volumeOn);
|
||||
}
|
||||
|
||||
btnKeyBinding.setGraphic(keyBindingGlyph);
|
||||
|
||||
// Add Buttons
|
||||
btns.getChildren().add(0, spacer);
|
||||
btns.getChildren().add(0, btnMute);
|
||||
btns.getChildren().add(0, btnKeyBinding);
|
||||
btnMute.setOnAction((action) -> {
|
||||
Sounds.toggleAllSounds();
|
||||
if (btnMute.getGraphic().equals(volumeOff)) {
|
||||
@@ -212,6 +242,7 @@ public class ViewManager {
|
||||
|
||||
/**
|
||||
* Change the view to the Lobby Screen
|
||||
*
|
||||
* @param disableReadyButton Boolean value so that clients can't try start a game.
|
||||
* @return A LobbyController object for the Lobby Screen.
|
||||
*/
|
||||
@@ -235,6 +266,7 @@ public class ViewManager {
|
||||
|
||||
/**
|
||||
* Sets up the view for the race. Creating a new decorator and destroying the old one.
|
||||
*
|
||||
* @return A RaceViewController for the race view screen.
|
||||
*/
|
||||
public RaceViewController loadRaceView() {
|
||||
@@ -258,15 +290,6 @@ public class ViewManager {
|
||||
scene.setOnKeyPressed(gameClient::keyPressed);
|
||||
scene.setOnKeyReleased(gameClient::keyReleased);
|
||||
|
||||
// uncomment to make it full screen
|
||||
// Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
|
||||
// stage.setX(visualBounds.getMinX());
|
||||
// stage.setY(visualBounds.getMinY());
|
||||
// stage.setWidth(visualBounds.getWidth());
|
||||
// stage.setHeight(visualBounds.getHeight());
|
||||
// stage.setMaximized(true);
|
||||
// stage.setFullScreen(true);
|
||||
|
||||
stage.setMinHeight(500);
|
||||
stage.setMinWidth(800);
|
||||
stage.setTitle("Party Parrots At Sea");
|
||||
@@ -289,4 +312,9 @@ public class ViewManager {
|
||||
|
||||
return loader.getController();
|
||||
}
|
||||
|
||||
public Stage getStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package seng302.visualiser.controllers.dialogs;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXToggleButton;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import seng302.model.GameKeyBind;
|
||||
import seng302.model.KeyAction;
|
||||
import seng302.visualiser.GameClient;
|
||||
import seng302.visualiser.controllers.ViewManager;
|
||||
|
||||
public class KeyBindingDialogController implements Initializable {
|
||||
|
||||
//--------FXML BEGIN--------//
|
||||
@FXML
|
||||
private Label keyBindingDialogHeader;
|
||||
@FXML
|
||||
private Label closeLabel;
|
||||
@FXML
|
||||
private JFXButton zoomInbtn;
|
||||
@FXML
|
||||
private JFXButton zoomOutBtn;
|
||||
@FXML
|
||||
private JFXButton vmgBtn;
|
||||
@FXML
|
||||
private JFXButton sailInOutBtn;
|
||||
@FXML
|
||||
private JFXButton tackGybeBtn;
|
||||
@FXML
|
||||
private JFXButton upwindBtn;
|
||||
@FXML
|
||||
private JFXButton downwindBtn;
|
||||
@FXML
|
||||
private JFXButton resetBtn;
|
||||
@FXML
|
||||
private Label upwindLabel;
|
||||
@FXML
|
||||
private Label downwindLabel;
|
||||
@FXML
|
||||
private JFXToggleButton turningToggle;
|
||||
//---------FXML END---------//
|
||||
|
||||
private GameKeyBind gameKeyBind;
|
||||
private List<JFXButton> buttons = new ArrayList<>();
|
||||
private Map<Button, KeyAction> buttonActionMap;
|
||||
private GameClient gameClient; // to send turning mode packet
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
gameKeyBind = GameKeyBind.getInstance();
|
||||
buttons = new ArrayList<>();
|
||||
Collections.addAll(buttons,
|
||||
zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn);
|
||||
bindButtonWithAction();
|
||||
loadKeyBind();
|
||||
|
||||
buttons.forEach(button -> {
|
||||
button.setOnMouseEntered(event -> mouseEnter(button));
|
||||
button.setOnMousePressed(event -> buttonPressed(button));
|
||||
button.setOnMouseExited(event -> mouseExit(button));
|
||||
button.setOnKeyPressed(event -> keyPressed(event, button));
|
||||
});
|
||||
|
||||
turningToggle.setOnMouseClicked(event -> toggleTurningMode());
|
||||
|
||||
resetBtn.setOnMouseClicked(event -> {
|
||||
gameKeyBind.setToDefault();
|
||||
loadKeyBind();
|
||||
});
|
||||
|
||||
closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog());
|
||||
|
||||
keyBindingDialogHeader.setFocusTraversable(true);
|
||||
keyBindingDialogHeader.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set buttons' label according to GameKeyBind settings
|
||||
*/
|
||||
private void loadKeyBind() {
|
||||
buttons.forEach(
|
||||
button -> button
|
||||
.setText(gameKeyBind.getKeyCode(buttonActionMap.get(button)).getName()));
|
||||
turningToggle.setSelected(gameKeyBind.isContinuouslyTurning());
|
||||
if (gameKeyBind.isContinuouslyTurning()) {
|
||||
upwindLabel.setText("ClOCKWISE TURNING");
|
||||
downwindLabel.setText("ANTICLOCKWISE TURNING");
|
||||
} else {
|
||||
upwindLabel.setText("UPWIND");
|
||||
downwindLabel.setText("DOWNWIND");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind buttons with specific action in a map.
|
||||
*/
|
||||
private void bindButtonWithAction() {
|
||||
buttonActionMap = new HashMap<>();
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buttonActionMap.put(buttons.get(i), KeyAction.getType(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt success / failure message for reassigning key action
|
||||
*/
|
||||
private void showSnackBar(String message, Boolean isWarning) {
|
||||
ViewManager.getInstance().showSnackbar(message, isWarning);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a mouse enters the button, the color and font size should change to highlight
|
||||
* @param button
|
||||
*/
|
||||
private void mouseEnter(Button button) {
|
||||
button.setStyle(""
|
||||
+ "-fx-background-color: -fx-pp-theme-color;"
|
||||
+ "-fx-text-fill: -fx-pp-front-color;"
|
||||
+ "-fx-font-size: 15;");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt "press key..." to inform users assign a new key bind by pressing a key
|
||||
* @param button
|
||||
*/
|
||||
private void buttonPressed(Button button) {
|
||||
button.setText("PRESS KEY...");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When mouse leaves the button, return the button to the normal state in terms of text,
|
||||
* color and font size
|
||||
* @param button
|
||||
*/
|
||||
private void mouseExit(Button button) {
|
||||
button.setText(GameKeyBind.getInstance().getKeyCode(buttonActionMap.get(button)).getName());
|
||||
button.setStyle(""
|
||||
+ "-fx-background-color: -fx-pp-front-color; "
|
||||
+ "-fx-text-fill: -fx-pp-theme-color; "
|
||||
+ "-fx-font-size: 13;");
|
||||
}
|
||||
|
||||
/**
|
||||
* When a key is pressed, check if the new binding conflicts to any existed settings, if not
|
||||
* assign the selected action with the new key binding to GameKeyBind.
|
||||
* @param event
|
||||
* @param button
|
||||
*/
|
||||
private void keyPressed(KeyEvent event, Button button) {
|
||||
event.consume();
|
||||
KeyAction buttonAction = buttonActionMap.get(button);
|
||||
if (gameKeyBind.bindKeyToAction(event.getCode(), buttonAction)) {
|
||||
showSnackBar(button.getId() + " is set to " + event.getCode().getName(), false);
|
||||
button.setText(gameKeyBind.getKeyCode(buttonAction).getName());
|
||||
} else {
|
||||
loadKeyBind();
|
||||
showSnackBar(event.getCode().getName() + " is already in use", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the turning mode is toggled, update gameKeyBind and send out packet to notify the server
|
||||
*/
|
||||
private void toggleTurningMode() {
|
||||
gameKeyBind.toggleTurningMode();
|
||||
gameClient.sendToggleTurningModePacket();
|
||||
loadKeyBind();
|
||||
}
|
||||
|
||||
public void setGameClient(GameClient gameClient) {
|
||||
this.gameClient = gameClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package seng302.visualiser.controllers.dialogs;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXCheckBox;
|
||||
import com.jfoenix.controls.JFXSlider;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.validation.RequiredFieldValidator;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import seng302.gameServer.ServerDescription;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.MapMaker;
|
||||
import seng302.visualiser.controllers.ViewManager;
|
||||
import seng302.visualiser.validators.FieldLengthValidator;
|
||||
import seng302.visualiser.validators.ValidationTools;
|
||||
@@ -28,13 +30,35 @@ public class ServerCreationController implements Initializable {
|
||||
private Label maxPlayersLabel;
|
||||
@FXML
|
||||
private JFXButton submitBtn;
|
||||
@FXML
|
||||
private JFXButton nextMapButton;
|
||||
@FXML
|
||||
private JFXButton lastMapButton;
|
||||
@FXML
|
||||
private Label mapNameLabel;
|
||||
@FXML
|
||||
private JFXSlider legsSlider;
|
||||
@FXML
|
||||
private Label legsSliderLabel;
|
||||
@FXML
|
||||
private JFXCheckBox pickupsCheckBox;
|
||||
@FXML
|
||||
private AnchorPane mapHolder;
|
||||
|
||||
private MapMaker mapMaker = MapMaker.getInstance();
|
||||
|
||||
//---------FXML END---------//
|
||||
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
legsSlider.setMax(10);
|
||||
maxPlayersSlider.valueProperty().addListener(
|
||||
(observable, oldValue, newValue) -> updateMaxPlayerLabel()
|
||||
);
|
||||
legsSlider.valueProperty().addListener(
|
||||
(obs, oldVal, newVal) -> updateLegSliderLabel()
|
||||
);
|
||||
updateMaxPlayerLabel();
|
||||
maxPlayersSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
updateMaxPlayerLabel();
|
||||
});
|
||||
updateLegSliderLabel();
|
||||
|
||||
FieldLengthValidator fieldLengthValidator = new FieldLengthValidator(40);
|
||||
fieldLengthValidator.setMessage("Server name too long.");
|
||||
@@ -49,6 +73,18 @@ public class ServerCreationController implements Initializable {
|
||||
validateServerSettings();
|
||||
});
|
||||
|
||||
nextMapButton.setOnMouseReleased(event -> {
|
||||
Sounds.playButtonClick();
|
||||
nextMap();
|
||||
});
|
||||
|
||||
lastMapButton.setOnMouseReleased(event -> {
|
||||
Sounds.playButtonClick();
|
||||
lastMap();
|
||||
});
|
||||
|
||||
mapHolder.getChildren().setAll(mapMaker.getCurrentGameView());
|
||||
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +105,7 @@ public class ServerCreationController implements Initializable {
|
||||
private void createServer() {
|
||||
ServerDescription serverDescription = ViewManager.getInstance().getGameClient()
|
||||
.runAsHost("localhost", 4941, serverName.getText(), (int) maxPlayersSlider
|
||||
.getValue());
|
||||
.getValue(), mapMaker.getCurrentRacePath(), (int) legsSlider.getValue());
|
||||
|
||||
ViewManager.getInstance().setProperty("serverName", serverDescription.getName());
|
||||
ViewManager.getInstance().setProperty("mapName", serverDescription.getMapName());
|
||||
@@ -80,11 +116,31 @@ public class ServerCreationController implements Initializable {
|
||||
*/
|
||||
private void updateMaxPlayerLabel() {
|
||||
maxPlayersSlider.setValue(Math.floor(maxPlayersSlider.getValue()));
|
||||
maxPlayersLabel.setText(String.format("YOU SELECTED: %.0f", maxPlayersSlider.getValue()));
|
||||
maxPlayersLabel.setText(String.format("Max players: %.0f", maxPlayersSlider.getValue()));
|
||||
}
|
||||
|
||||
private void updateLegSliderLabel() {
|
||||
legsSlider.setValue(Math.floor(legsSlider.getValue()));
|
||||
legsSliderLabel.setText(
|
||||
String.format("A section of the race will repeat %.0f times", legsSlider.getValue())
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public void playButtonHoverSound(MouseEvent mouseEvent) {
|
||||
Sounds.playHoverSound();
|
||||
}
|
||||
|
||||
private void nextMap() {
|
||||
mapMaker.next();
|
||||
mapHolder.getChildren().setAll(mapMaker.getCurrentGameView());
|
||||
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
|
||||
}
|
||||
|
||||
private void lastMap() {
|
||||
mapMaker.previous();
|
||||
mapHolder.getChildren().setAll(mapMaker.getCurrentGameView());
|
||||
mapNameLabel.setText(mapMaker.getCurrentRegatta().getCourseName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<configurations>
|
||||
<race-name>AC35</race-name>
|
||||
<race-size>6</race-size>
|
||||
<time-scale>10.0</time-scale>
|
||||
<windDir-direction>135</windDir-direction>
|
||||
</configurations>
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<markers>
|
||||
<marks>
|
||||
<gate>
|
||||
<name type="start-line">Start</name>
|
||||
<mark>
|
||||
<name>Start1</name>
|
||||
<latitude>57.6703330</latitude>
|
||||
<longitude>11.8278330</longitude>
|
||||
<id>122</id>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Start2</name>
|
||||
<latitude>57.6703330</latitude>
|
||||
<longitude>11.8271333</longitude>
|
||||
<id>123</id>
|
||||
</mark>
|
||||
</gate>
|
||||
<mark>
|
||||
<name>Mid Mark</name>
|
||||
<latitude>57.6675700</latitude>
|
||||
<longitude>11.8359880</longitude>
|
||||
<id>131</id>
|
||||
</mark>
|
||||
<gate>
|
||||
<name>Leeward Gate</name>
|
||||
<mark>
|
||||
<name>Leeward Gate1</name>
|
||||
<latitude>57.6708220</latitude>
|
||||
<longitude>11.8433900</longitude>
|
||||
<id>124</id>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Leeward Gate2</name>
|
||||
<latitude>57.6711220</latitude>
|
||||
<longitude>11.8436900</longitude>
|
||||
<id>125</id>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate>
|
||||
<name>Windward Gate</name>
|
||||
<mark>
|
||||
<name>Windward Gate1</name>
|
||||
<latitude>57.6650170</latitude>
|
||||
<longitude>11.8279170</longitude>
|
||||
<id>126</id>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Windward Gate2</name>
|
||||
<latitude>57.6653170</latitude>
|
||||
<longitude>11.8282170</longitude>
|
||||
<id>127</id>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate type="finish-line">
|
||||
<name>Finish</name>
|
||||
<mark>
|
||||
<name>Finish1</name>
|
||||
<latitude>57.6715240</latitude>
|
||||
<longitude>11.8444950</longitude>
|
||||
<id>128</id>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Finish2</name>
|
||||
<latitude>57.6718240</latitude>
|
||||
<longitude>11.8447950</longitude>
|
||||
<id>129</id>
|
||||
</mark>
|
||||
</gate>
|
||||
</marks>
|
||||
<order>
|
||||
<one>Start</one>
|
||||
<two>Mid Mark</two>
|
||||
<three>Leeward Gate</three>
|
||||
<four>Windward Gate</four>
|
||||
<five>Leeward Gate</five>
|
||||
<six>Finish</six>
|
||||
</order>
|
||||
</markers>
|
||||
@@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<course>
|
||||
<marks>
|
||||
<gate>
|
||||
<name type="start-line">Start</name>
|
||||
<mark>
|
||||
<name>Start1</name>
|
||||
<latitude>32.296577</latitude>
|
||||
<longitude>-64.854304</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Start2</name>
|
||||
<latitude>32.293771</latitude>
|
||||
<longitude>-64.855242</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<mark>
|
||||
<name>Mid Mark</name>
|
||||
<latitude>32.293039</latitude>
|
||||
<longitude>-64.843983</longitude>
|
||||
</mark>
|
||||
<gate>
|
||||
<name>Leeward Gate</name>
|
||||
<mark>
|
||||
<name>Leeward Gate1</name>
|
||||
<latitude>32.284680</latitude>
|
||||
<longitude>-64.850045</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Leeward Gate2</name>
|
||||
<latitude>32.280164</latitude>
|
||||
<longitude>-64.847591</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate>
|
||||
<name>Windward Gate</name>
|
||||
<mark>
|
||||
<name>Windward Gate1</name>
|
||||
<latitude>32.309693</latitude>
|
||||
<longitude>-64.835249</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Windward Gate2</name>
|
||||
<latitude>32.308046</latitude>
|
||||
<longitude>-64.831785</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate type="finish-line">
|
||||
<name>Finish</name>
|
||||
<mark>
|
||||
<name>Finish1</name>
|
||||
<latitude>32.317379</latitude>
|
||||
<longitude>-64.839291</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Finish2</name>
|
||||
<latitude>32.317257</latitude>
|
||||
<longitude>-64.836260</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
</marks>
|
||||
<order>
|
||||
<one>Start</one>
|
||||
<two>Mid Mark</two>
|
||||
<three>Leeward Gate</three>
|
||||
<four>Windward Gate</four>
|
||||
<five>Leeward Gate</five>
|
||||
<six>Finish</six>
|
||||
</order>
|
||||
</course>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<teams>
|
||||
<team>
|
||||
<name>Oracle Team USA</name>
|
||||
<alias>USA</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>102</id>
|
||||
</team>
|
||||
<team>
|
||||
<name>Artemis Racing</name>
|
||||
<alias>ART</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>101</id>
|
||||
</team>
|
||||
<team>
|
||||
<name>Emirates Team New Zealand</name>
|
||||
<alias>NZL</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>103</id>
|
||||
</team>
|
||||
<team>
|
||||
<name>Land Rover BAR</name>
|
||||
<alias>BAR</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>104</id>
|
||||
</team>
|
||||
<team>
|
||||
<name>SoftBank Team Japan</name>
|
||||
<alias>JAP</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>105</id>
|
||||
</team>
|
||||
<team>
|
||||
<name>Groupama Team France</name>
|
||||
<alias>FRC</alias>
|
||||
<currentVelocity>0.0</currentVelocity>
|
||||
<id>106</id>
|
||||
</team>
|
||||
</teams>
|
||||
@@ -44,6 +44,11 @@
|
||||
-fx-border-color: -fx-decorator-color;
|
||||
-fx-border-width: 0 4 4 4;
|
||||
}
|
||||
|
||||
.jfx-decorator-button {
|
||||
-fx-focus-traversable: false; /* so decorator button will not be focused */
|
||||
}
|
||||
|
||||
/********* customised scroll bar for scroll pane ***********/
|
||||
|
||||
/* The main scrollbar **track** CSS class */
|
||||
@@ -100,3 +105,15 @@
|
||||
.slider .track {
|
||||
-fx-background-color: -fx-pp-dark-text-color;
|
||||
}
|
||||
|
||||
.jfx-snackbar-content {
|
||||
-fx-background-color: -fx-pp-front-color;
|
||||
-fx-padding: 0 5 0 5;
|
||||
-fx-spacing: 0 5 0 5;
|
||||
-fx-font-size: 15;
|
||||
}
|
||||
|
||||
.jfx-snackbar-toast {
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
-fx-font-size: 15;
|
||||
}
|
||||
@@ -48,6 +48,7 @@ GridPane .timer * {
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
-fx-font-size: 13px;
|
||||
-fx-pref-height: 35px;
|
||||
-fx-focus-traversable: false;
|
||||
}
|
||||
|
||||
#chatSend:hover {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
-fx-font-size: 20px;
|
||||
-fx-text-fill: -fx-pp-light-text-color;
|
||||
-fx-background-color: -fx-pp-theme-color;
|
||||
-fx-focus-traversable: false;
|
||||
}
|
||||
|
||||
.jfx-rippler {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#keyBindingDialogHeader {
|
||||
-fx-font-size: 27px;
|
||||
-fx-text-fill: -fx-pp-dark-text-color;
|
||||
}
|
||||
|
||||
#closeLabel {
|
||||
-fx-font-size: 30;
|
||||
-fx-text-fill: -fx-pp-dark-text-color;
|
||||
}
|
||||
|
||||
#closeLabel:hover {
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
-fx-font-size: 33;
|
||||
}
|
||||
|
||||
JFXButton {
|
||||
-fx-background-color: -fx-pp-light-text-color;
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
-fx-font-size: 13px;
|
||||
}
|
||||
|
||||
Label {
|
||||
-fx-font-size: 15px;
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
-fx-effect: -fx-pp-dropshadow-light;
|
||||
}
|
||||
|
||||
JFXToggleButton {
|
||||
-jfx-toggle-color: -fx-pp-theme-color;
|
||||
-fx-text-fill: -fx-pp-theme-color;
|
||||
}
|
||||
|
||||
#resetBtn {
|
||||
-fx-background-color: -fx-pp-theme-color;
|
||||
-fx-text-fill: -fx-pp-front-color;
|
||||
-fx-effect: -fx-pp-dropshadow-light;
|
||||
-fx-font-size: 18;
|
||||
}
|
||||
|
||||
#resetBtn:hover {
|
||||
-fx-font-size: 20;
|
||||
}
|
||||
|
||||
.jfx-snackbar-content {
|
||||
-fx-background-color: #323232;
|
||||
}
|
||||
|
||||
.jfx-snackbar-toast {
|
||||
-fx-text-fill: WHITE;
|
||||
}
|
||||
|
||||
.jfx-snackbar-action {
|
||||
-fx-text-fill: #ff4081;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* a separate file to dynamically change snackbar's color */
|
||||
.jfx-snackbar-toast {
|
||||
-fx-text-fill: red !important;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RaceDefinition>
|
||||
|
||||
<CourseName> El Classico </CourseName>
|
||||
|
||||
<CentralLat> 57.6679590 </CentralLat>
|
||||
<CentralLng> 11.8503233 </CentralLng>
|
||||
|
||||
<Marks>
|
||||
<CompoundMark CompoundMarkID="1">
|
||||
<Mark Lat="57.670603" Lng="11.828262"/>
|
||||
<Mark Lat="57.669445" Lng="11.826413"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="2">
|
||||
<Mark Lat="57.6675700" Lng="11.8359880"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="3">
|
||||
<Mark Lat="57.6708220" Lng="11.8433900"/>
|
||||
<Mark Lat="57.671629" Lng="11.840951"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="4">
|
||||
<Mark Lat="57.664190" Lng="11.829576"/>
|
||||
<Mark Lat="57.665316" Lng="11.827184"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="5">
|
||||
<Mark Lat="57.672350" Lng="11.842535"/>
|
||||
<Mark Lat="57.6715240" Lng="11.8444950"/>
|
||||
</CompoundMark>
|
||||
</Marks>
|
||||
|
||||
<Course>
|
||||
<OpeningSegment>
|
||||
<Corner CompoundMarkID="1" Rounding="PS"/>
|
||||
<Corner CompoundMarkID="2" Rounding="P"/>
|
||||
</OpeningSegment>
|
||||
|
||||
<RepeatingSegment>
|
||||
<Corner CompoundMarkID="3" Rounding="SP"/>
|
||||
<Corner CompoundMarkID="4" Rounding="PS"/>
|
||||
</RepeatingSegment>
|
||||
|
||||
<ClosingSegment>
|
||||
<Corner CompoundMarkID="5" Rounding="PS"/>
|
||||
</ClosingSegment>
|
||||
</Course>
|
||||
|
||||
<CourseLimit>
|
||||
<Limit Lat="57.6739450" Lng="11.8417100" />
|
||||
<Limit Lat="57.6709520" Lng="11.8485010" />
|
||||
<Limit Lat="57.6690260" Lng="11.8472790" />
|
||||
<Limit Lat="57.6693140" Lng="11.8457610" />
|
||||
<Limit Lat="57.6665370" Lng="11.8432910" />
|
||||
<Limit Lat="57.6641400" Lng="11.8385840" />
|
||||
<Limit Lat="57.6629430" Lng="11.8332030" />
|
||||
<Limit Lat="57.6629480" Lng="11.8249660" />
|
||||
<Limit Lat="57.6686890" Lng="11.8250920" />
|
||||
<Limit Lat="57.6692230" Lng="11.8231430" />
|
||||
<Limit Lat="57.6725370" Lng="11.8272480" />
|
||||
<Limit Lat="57.6708220" Lng="11.8321340" />
|
||||
</CourseLimit>
|
||||
|
||||
</RaceDefinition>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RaceDefinition>
|
||||
|
||||
<CourseName> HorseShoe </CourseName>
|
||||
|
||||
<CentralLat> -14.6457 </CentralLat>
|
||||
<CentralLng> 47.612855 </CentralLng>
|
||||
|
||||
<Marks>
|
||||
<CompoundMark CompoundMarkID="1">
|
||||
<Mark Lat="-14.071412" Lng="47.05756"/>
|
||||
<Mark Lat="-14.070814" Lng="47.058041"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="2">
|
||||
<Mark Lat="-14.067194" Lng="47.053818" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="3">
|
||||
<Mark Lat="-14.061248" Lng="47.058556" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="4">
|
||||
<Mark Lat="-14.060584" Lng="47.063088"/>
|
||||
<Mark Lat="-14.060617" Lng="47.06374" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="5">
|
||||
<Mark Lat="-14.064637" Lng="47.060307"/>
|
||||
<Mark Lat="-14.06477" Lng="47.061165"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="6">
|
||||
<Mark Lat="-14.063839" Lng="47.06762"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="7">
|
||||
<Mark Lat="-14.068954" Lng="47.066349"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="8">
|
||||
<Mark Lat="-14.07025" Lng="47.060238"/>
|
||||
<Mark Lat="-14.071047" Lng="47.060307"/>
|
||||
</CompoundMark>
|
||||
</Marks>
|
||||
|
||||
<Course>
|
||||
|
||||
<OpeningSegment>
|
||||
<Corner CompoundMarkID="1" Rounding="PS"/>
|
||||
<Corner CompoundMarkID="2" Rounding="S"/>
|
||||
<Corner CompoundMarkID="3" Rounding="S"/>
|
||||
<Corner CompoundMarkID="4" Rounding="SP"/>
|
||||
</OpeningSegment>
|
||||
|
||||
<RepeatingSegment>
|
||||
<Corner CompoundMarkID="5" Rounding="SP"/>
|
||||
<Corner CompoundMarkID="4" Rounding="PS"/>
|
||||
</RepeatingSegment>
|
||||
|
||||
<ClosingSegment>
|
||||
<Corner CompoundMarkID="6" Rounding="S"/>
|
||||
<Corner CompoundMarkID="7" Rounding="S"/>
|
||||
<Corner CompoundMarkID="8" Rounding="SP" />
|
||||
</ClosingSegment>
|
||||
|
||||
</Course>
|
||||
|
||||
<CourseLimit>
|
||||
<Limit Lat="-14.073371" Lng="47.058213" />
|
||||
<Limit Lat="-14.06653" Lng="47.051003" />
|
||||
<Limit Lat="-14.059022" Lng="47.057286" />
|
||||
<Limit Lat="-14.058723" Lng="47.064358" />
|
||||
<Limit Lat="-14.06261" Lng="47.071293" />
|
||||
<Limit Lat="-14.070814" Lng="47.06762" />
|
||||
<Limit Lat="-14.072773" Lng="47.059689" />
|
||||
<Limit Lat="-14.068323" Lng="47.059449" />
|
||||
<Limit Lat="-14.064969" Lng="47.065113" />
|
||||
<Limit Lat="-14.066131" Lng="47.05986" />
|
||||
<Limit Lat="-14.063441" Lng="47.058419" />
|
||||
<Limit Lat="-14.068091" Lng="47.059174" />
|
||||
</CourseLimit>
|
||||
|
||||
</RaceDefinition>
|
||||
@@ -5,8 +5,8 @@
|
||||
<author>Blender User</author>
|
||||
<authoring_tool>Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22</authoring_tool>
|
||||
</contributor>
|
||||
<created>2017-09-11T16:51:03</created>
|
||||
<modified>2017-09-11T16:51:03</modified>
|
||||
<created>2017-09-26T01:05:25</created>
|
||||
<modified>2017-09-26T01:05:25</modified>
|
||||
<unit name="meter" meter="1"/>
|
||||
<up_axis>Z_UP</up_axis>
|
||||
</asset>
|
||||
@@ -48,7 +48,7 @@
|
||||
<geometry id="Plane_004-mesh" name="Plane.004">
|
||||
<mesh>
|
||||
<source id="Plane_004-mesh-positions">
|
||||
<float_array id="Plane_004-mesh-positions-array" count="240">-1 -0.09999954 0.01554524 1 -0.09999954 0.01554524 -1 0.1000005 0.01554524 1 0.1000005 0.01554524 1 0.1000005 0.01554524 1 -0.09999954 0.01554524 1.019509 -0.09807801 0.01554524 1.038269 -0.09238743 0.01554524 1.055557 -0.08314645 0.01554524 1.070711 -0.07071018 0.01554524 1.083147 -0.05555653 0.01554524 1.092388 -0.03826785 0.01554524 1.098079 -0.01950848 0.01554524 1.1 5.96046e-7 0.01554524 1.098079 0.01950955 0.01554524 1.092388 0.03826892 0.01554524 1.083147 0.0555576 0.01554524 1.070711 0.07071125 0.01554524 1.055557 0.08314752 0.01554524 1.038269 0.09238851 0.01554524 1.019509 0.09807896 0.01554524 -1 0.1000005 0.01554524 -1.019509 0.0980789 0.01554524 -1.038268 0.09238833 0.01554524 -1.055557 0.08314734 0.01554524 -1.070711 0.07071107 0.01554524 -1.083147 0.05555742 0.01554524 -1.092388 0.03826874 0.01554524 -1.098078 0.01950937 0.01554524 -1.1 4.56348e-7 0.01554524 -1.098078 -0.0195086 0.01554524 -1.092388 -0.03826785 0.01554524 -1.083147 -0.05555653 0.01554524 -1.070711 -0.07071018 0.01554524 -1.055557 -0.08314645 0.01554524 -1.038268 -0.09238755 0.01554524 -1.019509 -0.09807813 0.01554524 -0.9999997 -0.09999954 0.01554524 1 4.76837e-7 0.01554524 -1 4.76837e-7 0.01554524 -1 4.76837e-7 0.04452204 -1 0.1000005 0.04452204 -1 -0.09999954 0.04452204 1 -0.09999954 0.04452204 1 0.1000005 0.04452204 1.019509 -0.09807801 0.04452204 1 -0.09999954 0.04452204 1.038269 -0.09238743 0.04452204 1.055557 -0.08314645 0.04452204 1.070711 -0.07071018 0.04452204 1.083147 -0.05555653 0.04452204 1.092388 -0.03826785 0.04452204 1.098079 -0.01950848 0.04452204 1.1 5.96046e-7 0.04452204 1.098079 0.01950955 0.04452204 1.092388 0.03826892 0.04452204 1.083147 0.0555576 0.04452204 1.070711 0.07071125 0.04452204 1.055557 0.08314752 0.04452204 1.038269 0.09238851 0.04452204 1.019509 0.09807896 0.04452204 1 0.1000005 0.04452204 -1.019509 0.0980789 0.04452204 -1 0.1000005 0.04452204 -1.038268 0.09238833 0.04452204 -1.055557 0.08314734 0.04452204 -1.070711 0.07071107 0.04452204 -1.083147 0.05555742 0.04452204 -1.092388 0.03826874 0.04452204 -1.098078 0.01950937 0.04452204 -1.1 4.56348e-7 0.04452204 -1.098078 -0.0195086 0.04452204 -1.092388 -0.03826785 0.04452204 -1.083147 -0.05555653 0.04452204 -1.070711 -0.07071018 0.04452204 -1.055557 -0.08314645 0.04452204 -1.038268 -0.09238755 0.04452204 -1.019509 -0.09807813 0.04452204 -0.9999997 -0.09999954 0.04452204 1 4.76837e-7 0.04452204</float_array>
|
||||
<float_array id="Plane_004-mesh-positions-array" count="240">-1 -0.09999954 0.01554518 1 -0.09999954 0.01554518 -1 0.1000005 0.01554518 1 0.1000005 0.01554518 1 0.1000005 0.01554518 1 -0.09999954 0.01554518 1.019509 -0.09807801 0.01554518 1.038269 -0.09238743 0.01554518 1.055557 -0.08314645 0.01554518 1.070711 -0.07071018 0.01554518 1.083147 -0.05555647 0.01554518 1.092388 -0.03826785 0.01554518 1.098079 -0.01950848 0.01554518 1.1 5.96046e-7 0.01554518 1.098079 0.01950955 0.01554518 1.092388 0.03826892 0.01554518 1.083147 0.05555754 0.01554518 1.070711 0.07071125 0.01554518 1.055557 0.08314752 0.01554518 1.038269 0.09238851 0.01554518 1.019509 0.09807896 0.01554518 -1 0.1000005 0.01554518 -1.019509 0.0980789 0.01554518 -1.038268 0.09238833 0.01554518 -1.055557 0.08314734 0.01554518 -1.070711 0.07071107 0.01554518 -1.083147 0.05555737 0.01554518 -1.092388 0.03826874 0.01554518 -1.098078 0.01950937 0.01554518 -1.1 4.56348e-7 0.01554518 -1.098078 -0.0195086 0.01554518 -1.092388 -0.03826785 0.01554518 -1.083147 -0.05555647 0.01554518 -1.070711 -0.07071018 0.01554518 -1.055557 -0.08314645 0.01554518 -1.038268 -0.09238755 0.01554518 -1.019509 -0.09807813 0.01554518 -0.9999997 -0.09999954 0.01554518 1 4.76837e-7 0.01554518 -1 4.76837e-7 0.01554518 -1 4.76837e-7 0.04452198 -1 0.1000005 0.04452198 -1 -0.09999954 0.04452198 1 -0.09999954 0.04452198 1 0.1000005 0.04452198 1.019509 -0.09807801 0.04452198 1 -0.09999954 0.04452198 1.038269 -0.09238743 0.04452198 1.055557 -0.08314645 0.04452198 1.070711 -0.07071018 0.04452198 1.083147 -0.05555647 0.04452198 1.092388 -0.03826785 0.04452198 1.098079 -0.01950848 0.04452198 1.1 5.96046e-7 0.04452198 1.098079 0.01950955 0.04452198 1.092388 0.03826892 0.04452198 1.083147 0.05555754 0.04452198 1.070711 0.07071125 0.04452198 1.055557 0.08314752 0.04452198 1.038269 0.09238851 0.04452198 1.019509 0.09807896 0.04452198 1 0.1000005 0.04452198 -1.019509 0.0980789 0.04452198 -1 0.1000005 0.04452198 -1.038268 0.09238833 0.04452198 -1.055557 0.08314734 0.04452198 -1.070711 0.07071107 0.04452198 -1.083147 0.05555737 0.04452198 -1.092388 0.03826874 0.04452198 -1.098078 0.01950937 0.04452198 -1.1 4.56348e-7 0.04452198 -1.098078 -0.0195086 0.04452198 -1.092388 -0.03826785 0.04452198 -1.083147 -0.05555647 0.04452198 -1.070711 -0.07071018 0.04452198 -1.055557 -0.08314645 0.04452198 -1.038268 -0.09238755 0.04452198 -1.019509 -0.09807813 0.04452198 -0.9999997 -0.09999954 0.04452198 1 4.76837e-7 0.04452198</float_array>
|
||||
<technique_common>
|
||||
<accessor source="#Plane_004-mesh-positions-array" count="80" stride="3">
|
||||
<param name="X" type="float"/>
|
||||
@@ -58,9 +58,9 @@
|
||||
</technique_common>
|
||||
</source>
|
||||
<source id="Plane_004-mesh-normals">
|
||||
<float_array id="Plane_004-mesh-normals-array" count="342">0 0 -1 0 0 -1 0 0 -1 0 0 -1 1.19345e-7 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 -1.19344e-7 0 -1 0 0 1 -1.19345e-7 0 1 0 0 1 1.19344e-7 0 1 -4.77372e-7 0 1 0 0 1 0 0 1 0 0 1 -2.38688e-7 0 1 0 0 1 2.3869e-7 0 1 1.19345e-7 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 -2.3869e-7 0 1 0 0 1 -0.6343874 0.7730153 0 -1 0 0 0.9569399 -0.2902863 0 1 0 0 -0.7730182 0.6343839 0 0.9951848 -0.09801703 0 -0.8819218 0.4713957 0 0.9951835 0.09803056 0 -1 -5.14244e-6 0 -0.9569436 0.2902743 0 0.9569399 0.2902863 0 -0.9951834 0.09803056 0 0.8819165 0.4714059 0 0.9238785 0.3826861 0 -0.9951835 -0.09802997 0 0.7730053 0.6343997 0 0.3826843 -0.9238792 0 0 -1 0 -0.9569393 -0.2902879 0 0.6344003 0.7730048 0 0 1 0 -0.8819164 -0.4714059 0 0.4713947 0.8819224 0 0.09802103 -0.9951844 0 -0.7730182 -0.6343839 0 1 3.85683e-6 0 0.2902728 0.9569441 0 -0.2902856 0.9569401 0 -0.6343874 -0.7730153 0 0.09802168 0.9951844 0 -0.4713947 0.8819224 0 -0.4713994 -0.8819198 0 -0.09802168 0.9951844 0 0.6344003 -0.7730048 0 -0.2902857 -0.9569401 0 1 2.57122e-6 0 -0.2902857 0.9569401 0 0.7730085 -0.6343957 0 -0.0980131 -0.9951851 0 -0.4713975 0.881921 0 0.8819219 -0.4713957 0 0.9569398 -0.2902863 0 -0.7730182 0.634384 0 0.9951835 -0.09802997 0 -0.8819165 0.4714059 0 0.9951847 0.09801757 0 -1 -3.85683e-6 0 -0.9569399 0.2902863 0 0.9569398 0.2902863 0 -0.9951834 0.09803056 0 0.8819218 0.4713957 0 0.9238789 0.3826851 0 0.7730085 0.6343957 0 0.3826831 -0.9238798 0 -0.956943 -0.2902759 0 0.6344001 0.7730049 0 -0.8819219 -0.4713957 0 0.4713975 0.881921 0 0.09802103 -0.9951844 0 -0.7730182 -0.634384 0 1 3.85683e-6 0 -0.2902855 0.9569401 0 0.09802103 0.9951844 0 -0.4714021 -0.8819184 0 -0.09802103 0.9951844 0 0.6344001 -0.7730049 0 -0.2902856 -0.9569401 0 1 2.57122e-6 0 -0.2902856 0.9569401 0 0.7730054 -0.6343996 0 -0.09801179 -0.9951853 0 -0.4713947 0.8819224 0 0.8819164 -0.471406 0</float_array>
|
||||
<float_array id="Plane_004-mesh-normals-array" count="270">0 0 -1 1.19345e-7 0 -1 0 0 -1 0 0 -1 -1.19344e-7 0 -1 -1.19343e-7 0 -1 0 0 -1 0 0 -1 -1.19343e-7 0 -1 0 0 1 1.19345e-7 0 1 0 0 1 -4.77394e-7 0 1 -1.19344e-7 0 1 -4.77375e-7 0 1 4.77387e-7 0 1 0 0 1 -1.19345e-7 0 1 2.38692e-7 0 1 2.38687e-7 0 1 -2.38687e-7 0 1 -0.6343871 0.7730156 0 -1 0 0 0.9569398 -0.2902864 0 1 0 0 -0.7730116 0.6343919 0 0.9951861 -0.09800404 0 -0.8819217 0.4713962 0 0.9951872 0.09799164 0 -0.9569509 0.2902504 0 0.9569326 0.2903105 0 -0.9951821 0.09804385 0 0.8819217 0.471396 0 0.923877 0.3826896 0 -0.9951823 -0.09804314 0 0.773018 0.6343841 0 0.3826818 -0.9238802 0 0 -1 0 -0.9569467 -0.2902641 0 0.6343807 0.7730209 0 0 1 0 -0.8819217 -0.4713962 0 0.4714139 0.8819121 0 0.09802061 -0.9951844 0 -0.7730181 -0.6343839 0 1 3.85683e-6 0 0.2902668 0.9569458 0 -0.2902731 0.9569439 0 -0.6343807 -0.7730209 0 0.09802073 0.9951844 0 -0.4714134 0.8819125 0 -0.4713908 -0.8819245 0 -0.09802436 0.995184 0 0.6343871 -0.7730156 0 -0.2902895 -0.956939 0 -0.2902888 0.9569392 0 0.7730117 -0.6343918 0 -0.09801268 -0.9951853 0 -0.4713921 0.8819237 0 0.8819217 -0.4713962 0 -0.6343807 0.7730209 0 0.9569326 -0.2903105 0 -0.7730181 0.634384 0 0.9951873 -0.0979911 0 0.995186 0.09800463 0 -0.9569472 0.2902624 0 0.9569398 0.2902864 0 -0.9951821 0.09804385 0 0.923877 0.3826897 0 0.7730115 0.634392 0 0.3826841 -0.9238793 0 -0.9569503 -0.2902522 0 0.6343871 0.7730156 0 0.4714112 0.8819137 0 0.09801995 -0.9951846 0 -0.7730117 -0.6343919 0 1 2.57122e-6 0 0.2902686 0.9569453 0 -0.2902749 0.9569434 0 -0.6343871 -0.7730156 0 0.09802132 0.9951844 0 -0.4714105 0.8819139 0 -0.4713962 -0.8819217 0 -0.09802371 0.9951841 0 0.6343807 -0.7730209 0 -0.2902895 -0.956939 0 -0.2902888 0.9569392 0 0.7730181 -0.634384 0 -0.09801262 -0.9951853 0 -0.4713866 0.8819267 0</float_array>
|
||||
<technique_common>
|
||||
<accessor source="#Plane_004-mesh-normals-array" count="114" stride="3">
|
||||
<accessor source="#Plane_004-mesh-normals-array" count="90" stride="3">
|
||||
<param name="X" type="float"/>
|
||||
<param name="Y" type="float"/>
|
||||
<param name="Z" type="float"/>
|
||||
@@ -74,7 +74,7 @@
|
||||
<input semantic="VERTEX" source="#Plane_004-mesh-vertices" offset="0"/>
|
||||
<input semantic="NORMAL" source="#Plane_004-mesh-normals" offset="1"/>
|
||||
<vcount>3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 </vcount>
|
||||
<p>38 0 0 0 39 0 6 0 5 0 38 0 8 1 6 1 38 1 9 2 38 2 10 2 8 0 38 0 9 0 10 0 38 0 11 0 11 0 38 0 12 0 12 3 38 3 13 3 4 0 20 0 38 0 19 0 38 0 20 0 18 4 38 4 19 4 17 0 38 0 18 0 16 0 38 0 17 0 15 0 38 0 16 0 14 0 38 0 15 0 13 0 38 0 14 0 37 5 36 5 39 5 35 6 39 6 36 6 21 7 39 7 22 7 34 8 39 8 35 8 33 9 39 9 34 9 32 10 39 10 33 10 31 11 39 11 32 11 30 12 39 12 31 12 29 13 39 13 30 13 28 14 39 14 29 14 27 15 39 15 28 15 26 16 39 16 27 16 25 17 39 17 26 17 22 18 39 18 23 18 23 19 39 19 24 19 24 20 39 20 25 20 40 21 43 21 79 21 45 21 79 21 46 21 48 21 79 21 45 21 49 21 50 21 79 21 48 21 49 21 79 21 50 22 51 22 79 22 51 21 52 21 79 21 52 23 53 23 79 23 61 21 79 21 60 21 59 21 60 21 79 21 58 21 59 21 79 21 57 21 58 21 79 21 56 21 57 21 79 21 55 21 56 21 79 21 54 24 55 24 79 24 53 21 54 21 79 21 78 25 40 25 77 25 76 26 77 26 40 26 63 27 62 27 40 27 75 28 76 28 40 28 74 29 75 29 40 29 73 30 74 30 40 30 72 31 73 31 40 31 71 32 72 32 40 32 70 33 71 33 40 33 69 34 70 34 40 34 68 35 69 35 40 35 67 36 68 36 40 36 66 37 67 37 40 37 62 38 64 38 40 38 64 39 65 39 40 39 65 40 66 40 40 40 25 41 65 41 24 41 38 42 61 42 4 42 12 43 51 43 11 43 38 44 43 44 1 44 26 45 66 45 25 45 13 46 52 46 12 46 0 42 40 42 39 42 27 47 67 47 26 47 14 48 53 48 13 48 5 49 79 49 38 49 28 50 68 50 27 50 15 51 54 51 14 51 29 52 69 52 28 52 16 53 55 53 15 53 38 54 47 54 7 54 39 42 41 42 2 42 30 55 70 55 29 55 17 56 56 56 16 56 8 57 45 57 6 57 1 58 42 58 0 58 31 59 71 59 30 59 18 60 57 60 17 60 2 61 44 61 3 61 32 62 72 62 31 62 19 63 58 63 18 63 6 64 46 64 5 64 33 65 73 65 32 65 37 66 40 66 78 66 20 67 59 67 19 67 6 68 47 68 7 68 34 69 74 69 33 69 4 70 60 70 20 70 7 71 48 71 8 71 35 72 75 72 34 72 22 73 63 73 21 73 9 74 48 74 8 74 36 75 76 75 35 75 21 76 40 76 39 76 23 77 62 77 22 77 10 78 49 78 9 78 37 79 77 79 36 79 24 80 64 80 23 80 11 81 50 81 10 81 3 44 79 44 38 44 39 0 2 0 3 0 38 0 1 0 0 0 39 0 3 0 38 0 44 21 41 21 40 21 40 21 42 21 43 21 79 21 44 21 40 21 25 41 66 41 65 41 38 42 79 42 61 42 12 82 52 82 51 82 38 44 79 44 43 44 26 83 67 83 66 83 13 84 53 84 52 84 0 42 42 42 40 42 27 85 68 85 67 85 14 86 54 86 53 86 5 87 46 87 79 87 28 88 69 88 68 88 15 89 55 89 54 89 29 90 70 90 69 90 16 91 56 91 55 91 38 92 79 92 47 92 39 42 40 42 41 42 30 55 71 55 70 55 17 93 57 93 56 93 8 94 48 94 45 94 1 58 43 58 42 58 31 95 72 95 71 95 18 96 58 96 57 96 2 61 41 61 44 61 32 97 73 97 72 97 19 98 59 98 58 98 6 99 45 99 46 99 33 100 74 100 73 100 37 101 39 101 40 101 20 67 60 67 59 67 6 102 45 102 47 102 34 69 75 69 74 69 4 103 61 103 60 103 7 80 47 80 48 80 35 104 76 104 75 104 22 105 62 105 63 105 9 106 49 106 48 106 36 107 77 107 76 107 21 108 63 108 40 108 23 109 64 109 62 109 10 110 50 110 49 110 37 111 78 111 77 111 24 112 65 112 64 112 11 113 51 113 50 113 3 44 44 44 79 44</p>
|
||||
<p>38 0 0 0 39 0 6 1 5 1 38 1 8 0 6 0 38 0 9 2 38 2 10 2 8 0 38 0 9 0 10 0 38 0 11 0 11 0 38 0 12 0 12 3 38 3 13 3 4 0 20 0 38 0 19 0 38 0 20 0 18 0 38 0 19 0 17 0 38 0 18 0 16 0 38 0 17 0 15 0 38 0 16 0 14 0 38 0 15 0 13 0 38 0 14 0 37 4 36 4 39 4 35 0 39 0 36 0 21 0 39 0 22 0 34 0 39 0 35 0 33 5 39 5 34 5 32 0 39 0 33 0 31 0 39 0 32 0 30 0 39 0 31 0 29 0 39 0 30 0 28 6 39 6 29 6 27 0 39 0 28 0 26 0 39 0 27 0 25 7 39 7 26 7 22 0 39 0 23 0 23 0 39 0 24 0 24 8 39 8 25 8 40 9 43 9 79 9 45 9 79 9 46 9 48 9 79 9 45 9 49 9 50 9 79 9 48 9 49 9 79 9 50 10 51 10 79 10 51 9 52 9 79 9 52 11 53 11 79 11 61 9 79 9 60 9 59 9 60 9 79 9 58 12 59 12 79 12 57 9 58 9 79 9 56 9 57 9 79 9 55 9 56 9 79 9 54 13 55 13 79 13 53 9 54 9 79 9 78 14 40 14 77 14 76 15 77 15 40 15 63 9 62 9 40 9 75 9 76 9 40 9 74 9 75 9 40 9 73 9 74 9 40 9 72 9 73 9 40 9 71 9 72 9 40 9 70 9 71 9 40 9 69 16 70 16 40 16 68 9 69 9 40 9 67 17 68 17 40 17 66 18 67 18 40 18 62 9 64 9 40 9 64 19 65 19 40 19 65 20 66 20 40 20 25 21 65 21 24 21 38 22 61 22 4 22 12 23 51 23 11 23 38 24 43 24 1 24 26 25 66 25 25 25 13 26 52 26 12 26 0 22 40 22 39 22 27 27 67 27 26 27 14 28 53 28 13 28 5 22 79 22 38 22 28 29 68 29 27 29 15 30 54 30 14 30 29 31 69 31 28 31 16 32 55 32 15 32 38 33 47 33 7 33 39 22 41 22 2 22 30 34 70 34 29 34 17 35 56 35 16 35 8 36 45 36 6 36 1 37 42 37 0 37 31 38 71 38 30 38 18 39 57 39 17 39 2 40 44 40 3 40 32 41 72 41 31 41 19 42 58 42 18 42 6 43 46 43 5 43 33 44 73 44 32 44 37 45 40 45 78 45 20 46 59 46 19 46 6 47 47 47 7 47 34 48 74 48 33 48 4 49 60 49 20 49 7 50 48 50 8 50 35 51 75 51 34 51 22 52 63 52 21 52 9 53 48 53 8 53 36 54 76 54 35 54 21 24 40 24 39 24 23 55 62 55 22 55 10 56 49 56 9 56 37 57 77 57 36 57 24 58 64 58 23 58 11 59 50 59 10 59 3 24 79 24 38 24 39 0 2 0 3 0 38 0 1 0 0 0 39 0 3 0 38 0 44 9 41 9 40 9 40 9 42 9 43 9 79 9 44 9 40 9 25 60 66 60 65 60 38 22 79 22 61 22 12 61 52 61 51 61 38 24 79 24 43 24 26 62 67 62 66 62 13 63 53 63 52 63 0 22 42 22 40 22 27 27 68 27 67 27 14 64 54 64 53 64 5 22 46 22 79 22 28 65 69 65 68 65 15 66 55 66 54 66 29 67 70 67 69 67 16 32 56 32 55 32 38 68 79 68 47 68 39 22 40 22 41 22 30 34 71 34 70 34 17 69 57 69 56 69 8 70 48 70 45 70 1 37 43 37 42 37 31 71 72 71 71 71 18 72 58 72 57 72 2 40 41 40 44 40 32 41 73 41 72 41 19 73 59 73 58 73 6 74 45 74 46 74 33 75 74 75 73 75 37 76 39 76 40 76 20 77 60 77 59 77 6 78 45 78 47 78 34 79 75 79 74 79 4 80 61 80 60 80 7 81 47 81 48 81 35 82 76 82 75 82 22 83 62 83 63 83 9 84 49 84 48 84 36 85 77 85 76 85 21 24 63 24 40 24 23 86 64 86 62 86 10 87 50 87 49 87 37 88 78 88 77 88 24 89 65 89 64 89 11 59 51 59 50 59 3 24 44 24 79 24</p>
|
||||
</polylist>
|
||||
</mesh>
|
||||
</geometry>
|
||||
@@ -83,7 +83,7 @@
|
||||
<library_visual_scenes>
|
||||
<visual_scene id="Scene" name="Scene">
|
||||
<node id="Plane" name="Plane" type="NODE">
|
||||
<matrix sid="transform">1 0 0 0 0 1 0 -4.76837e-7 0 0 1 0 0 0 0 1</matrix>
|
||||
<matrix sid="transform">0.4856636 0 0 0 0 0.6802911 0 -4.76837e-7 0 0 1 0 0 0 0 1</matrix>
|
||||
<instance_geometry url="#Plane_004-mesh" name="Plane">
|
||||
<bind_material>
|
||||
<technique_common>
|
||||
|
||||
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 59 KiB |
@@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BoatConfig>
|
||||
<Modified>2015-08-28T17:32:59+0100</Modified>
|
||||
<Version>12</Version>
|
||||
<Snapshot>219</Snapshot>
|
||||
<Settings>
|
||||
<RaceBoatType Type="AC45"/>
|
||||
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
|
||||
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
|
||||
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
|
||||
</Settings>
|
||||
<BoatShapes>
|
||||
<BoatShape ShapeID="0">
|
||||
<Vertices>
|
||||
<Vtx Seq="3" Y="25" X="0"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="14">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-1"/>
|
||||
<Vtx Seq="2" Y="0.75" X="-1"/>
|
||||
<Vtx Seq="3" Y="0.75" X="-0.25"/>
|
||||
<Vtx Seq="4" Y="3.5" X="-0.25"/>
|
||||
<Vtx Seq="5" Y="4.5" X="-1"/>
|
||||
<Vtx Seq="6" Y="6.5" X="-1"/>
|
||||
<Vtx Seq="7" Y="7" X="-0.5"/>
|
||||
<Vtx Seq="8" Y="7" X="0.5"/>
|
||||
<Vtx Seq="9" Y="6.5" X="1"/>
|
||||
<Vtx Seq="10" Y="4.5" X="1"/>
|
||||
<Vtx Seq="11" Y="3.5" X="0.25"/>
|
||||
<Vtx Seq="12" Y="0.75" X="0.25"/>
|
||||
<Vtx Seq="13" Y="0.75" X="1"/>
|
||||
<Vtx Seq="14" Y="0" X="1"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="15">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-3.46"/>
|
||||
<Vtx Seq="2" Y="13.449" X="-3.46"/>
|
||||
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||
<Vtx Seq="4" Y="13.449" X="3.46"/>
|
||||
<Vtx Seq="5" Y="0" X="3.46"/>
|
||||
</Vertices>
|
||||
<Catamaran>
|
||||
<Vtx Seq="1" Y="1.769" X="-2.752"/>
|
||||
<Vtx Seq="2" Y="0" X="-2.813"/>
|
||||
<Vtx Seq="3" Y="0" X="-3.34"/>
|
||||
<Vtx Seq="4" Y="5.351" X="-3.46"/>
|
||||
<Vtx Seq="5" Y="10.544" X="-3.387"/>
|
||||
<Vtx Seq="6" Y="13.449" X="-3.075"/>
|
||||
<Vtx Seq="7" Y="10.851" X="-2.793"/>
|
||||
<Vtx Seq="8" Y="6.669" X="-2.699"/>
|
||||
<Vtx Seq="9" Y="6.669" X="2.699"/>
|
||||
<Vtx Seq="10" Y="10.851" X="2.793"/>
|
||||
<Vtx Seq="11" Y="13.449" X="3.075"/>
|
||||
<Vtx Seq="12" Y="10.544" X="3.387"/>
|
||||
<Vtx Seq="13" Y="5.351" X="3.46"/>
|
||||
<Vtx Seq="14" Y="0" X="3.34"/>
|
||||
<Vtx Seq="15" Y="0" X="2.813"/>
|
||||
<Vtx Seq="16" Y="1.769" X="2.752"/>
|
||||
</Catamaran>
|
||||
<Bowsprit>
|
||||
<Vtx Seq="1" Y="6.669" X="-0.2"/>
|
||||
<Vtx Seq="2" Y="11.377" X="-0.2"/>
|
||||
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||
<Vtx Seq="4" Y="11.377" X="0.2"/>
|
||||
<Vtx Seq="5" Y="6.669" X="0.2"/>
|
||||
</Bowsprit>
|
||||
<Trampoline>
|
||||
<Vtx Seq="1" Y="2" X="-2.699"/>
|
||||
<Vtx Seq="2" Y="6.438" X="-2.699"/>
|
||||
<Vtx Seq="3" Y="6.438" X="2.699"/>
|
||||
<Vtx Seq="4" Y="2" X="2.699"/>
|
||||
</Trampoline>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="18">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-1.04"/>
|
||||
<Vtx Seq="2" Y="0.11" X="-1.18"/>
|
||||
<Vtx Seq="3" Y="0.42" X="-1.28"/>
|
||||
<Vtx Seq="4" Y="3.74" X="-1.29"/>
|
||||
<Vtx Seq="5" Y="5.36" X="-1.21"/>
|
||||
<Vtx Seq="6" Y="6.29" X="-1.08"/>
|
||||
<Vtx Seq="7" Y="7.15" X="-0.84"/>
|
||||
<Vtx Seq="8" Y="7.63" X="-0.62"/>
|
||||
<Vtx Seq="9" Y="7.94" X="-0.34"/>
|
||||
<Vtx Seq="10" Y="8.06" X="0"/>
|
||||
<Vtx Seq="11" Y="7.94" X="0.34"/>
|
||||
<Vtx Seq="12" Y="7.63" X="0.62"/>
|
||||
<Vtx Seq="13" Y="7.15" X="0.84"/>
|
||||
<Vtx Seq="14" Y="6.29" X="1.08"/>
|
||||
<Vtx Seq="15" Y="5.36" X="1.21"/>
|
||||
<Vtx Seq="16" Y="3.74" X="1.29"/>
|
||||
<Vtx Seq="17" Y="0.42" X="1.28"/>
|
||||
<Vtx Seq="18" Y="0.11" X="1.18"/>
|
||||
<Vtx Seq="19" Y="0" X="1.04"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="24">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-2.5"/>
|
||||
<Vtx Seq="2" Y="7" X="-2.5"/>
|
||||
<Vtx Seq="3" Y="12.6" X="-2.2"/>
|
||||
<Vtx Seq="4" Y="12.6" X="2.2"/>
|
||||
<Vtx Seq="5" Y="7" X="2.5"/>
|
||||
<Vtx Seq="6" Y="0" X="2.5"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="34">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-1.16"/>
|
||||
<Vtx Seq="2" Y="5.51" X="-1.16"/>
|
||||
<Vtx Seq="3" Y="5.846" X="-0.84"/>
|
||||
<Vtx Seq="4" Y="5.846" X="0.84"/>
|
||||
<Vtx Seq="5" Y="5.51" X="1.16"/>
|
||||
<Vtx Seq="6" Y="0" X="1.16"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
<BoatShape ShapeID="35">
|
||||
<Vertices>
|
||||
<Vtx Seq="1" Y="0" X="-1.461"/>
|
||||
<Vtx Seq="2" Y="6" X="-1.461"/>
|
||||
<Vtx Seq="3" Y="7" X="-1.44"/>
|
||||
<Vtx Seq="4" Y="8" X="-1.38"/>
|
||||
<Vtx Seq="5" Y="9" X="-1.17"/>
|
||||
<Vtx Seq="6" Y="10" X="-0.76"/>
|
||||
<Vtx Seq="7" Y="10.6" X="-0.34"/>
|
||||
<Vtx Seq="8" Y="10.61" X="0"/>
|
||||
<Vtx Seq="9" Y="10.6" X="0.34"/>
|
||||
<Vtx Seq="10" Y="10" X="0.76"/>
|
||||
<Vtx Seq="11" Y="9" X="1.17"/>
|
||||
<Vtx Seq="12" Y="8" X="1.38"/>
|
||||
<Vtx Seq="13" Y="7" X="1.44"/>
|
||||
<Vtx Seq="14" Y="6" X="1.461"/>
|
||||
<Vtx Seq="15" Y="0" X="1.461"/>
|
||||
</Vertices>
|
||||
</BoatShape>
|
||||
</BoatShapes>
|
||||
<Boats>
|
||||
<Boat Type="Yacht" SourceID="101" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
|
||||
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
<Boat Type="Yacht" SourceID="102" ShapeID="15" StoweName="SWE" ShortName="ARTEMIS" ShorterName="SWE" BoatName="ARTEMIS RACING" HullNum="AC4517" Skipper="OUTTERIDGE" Helmsman="OUTTERIDGE" Country="SWE" PeliID="102" RadioIP="172.20.2.102">
|
||||
<GPSposition Z="1.727" Y="-0.359" X="-0.0121"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
<Boat Type="Yacht" SourceID="103" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
|
||||
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
<Boat Type="Yacht" SourceID="104" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
|
||||
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
<Boat Type="Yacht" SourceID="105" ShapeID="15" StoweName="FRA" ShortName="FRANCE" ShorterName="FRA" BoatName="GROUPAMA TEAM FRANCE" HullNum="AC4505" Skipper="CAMMAS" Helmsman="CAMMAS" Country="FRA" PeliID="105" RadioIP="172.20.2.105">
|
||||
<GPSposition Z="1.863" Y="-0.3" X="-0.003"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
<Boat Type="Yacht" SourceID="106" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
|
||||
<GPSposition Z="1.734" Y="-0.352" X="0"/>
|
||||
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||
</Boat>
|
||||
</Boats>
|
||||
</BoatConfig>
|
||||
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Race>
|
||||
<CreationTimeDate>2015-08-29T13:12:40+02:00</CreationTimeDate>
|
||||
<RaceStartTime Start="2015-08-29T13:10:00+02:00" Postpone="False"/>
|
||||
<RaceID>15082901</RaceID>
|
||||
<RaceType>Fleet</RaceType>
|
||||
<Participants>
|
||||
<Yacht SourceID="101"/>
|
||||
<Yacht SourceID="102"/>
|
||||
<Yacht SourceID="103"/>
|
||||
<Yacht SourceID="104"/>
|
||||
<Yacht SourceID="105"/>
|
||||
<Yacht SourceID="106"/>
|
||||
</Participants>
|
||||
<Course>
|
||||
<CompoundMark CompoundMarkID="1" Name="Mark0">
|
||||
<Mark SeqID="1" Name="Start Line 1" TargetLat="57.6703330" TargetLng="11.8278330"
|
||||
SourceID="122"/>
|
||||
<Mark SeqID="2" Name="Start Line 2" TargetLat="57.6703330" TargetLng="11.8278330"
|
||||
SourceID="123"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="2" Name="Mark1">
|
||||
<Mark SeqID="1" Name="Mark1" TargetLat="57.6675700" TargetLng="11.8359880" SourceID="131"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="3" Name="Mark2">
|
||||
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="124"/>
|
||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="125"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="4" Name="Mark3">
|
||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="126"/>
|
||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="127"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="5" Name="Mark2">
|
||||
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="124"/>
|
||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="125"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="6" Name="Mark3">
|
||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="126"/>
|
||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="127"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="7" Name="Mark2">
|
||||
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="124"/>
|
||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="125"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="8" Name="Mark3">
|
||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="126"/>
|
||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="127"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="9" Name="Mark2">
|
||||
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="124"/>
|
||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
|
||||
SourceID="125"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="10" Name="Mark3">
|
||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="126"/>
|
||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
|
||||
SourceID="127"/>
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="11" Name="Mark4">
|
||||
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.6715240" TargetLng="11.8444950"
|
||||
SourceID="128"/>
|
||||
<Mark SeqID="2" Name="Finish Line 2" TargetLat="57.6715240" TargetLng="11.8444950"
|
||||
SourceID="129"/>
|
||||
</CompoundMark>
|
||||
</Course>
|
||||
<CompoundMarkSequence>
|
||||
<Corner SeqID="1" CompoundMarkID="1" Rounding="PS" ZoneSize="3"/>
|
||||
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3"/>
|
||||
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3"/>
|
||||
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3"/>
|
||||
<Corner SeqID="5" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
|
||||
<Corner SeqID="6" CompoundMarkID="6" Rounding="PS" ZoneSize="3"/>
|
||||
<Corner SeqID="7" CompoundMarkID="7" Rounding="SP" ZoneSize="3"/>
|
||||
<Corner SeqID="8" CompoundMarkID="8" Rounding="PS" ZoneSize="3"/>
|
||||
<Corner SeqID="9" CompoundMarkID="9" Rounding="SP" ZoneSize="3"/>
|
||||
<Corner SeqID="10" CompoundMarkID="10" Rounding="PS" ZoneSize="3"/>
|
||||
<Corner SeqID="11" CompoundMarkID="11" Rounding="PS" ZoneSize="3"/>
|
||||
</CompoundMarkSequence>
|
||||
<CourseLimit>
|
||||
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100"/>
|
||||
<Limit SeqID="2" Lat="57.6709520" Lon="11.8485010"/>
|
||||
<Limit SeqID="3" Lat="57.6690260" Lon="11.8472790"/>
|
||||
<Limit SeqID="4" Lat="57.6693140" Lon="11.8457610"/>
|
||||
<Limit SeqID="5" Lat="57.6665370" Lon="11.8432910"/>
|
||||
<Limit SeqID="6" Lat="57.6641400" Lon="11.8385840"/>
|
||||
<Limit SeqID="7" Lat="57.6629430" Lon="11.8332030"/>
|
||||
<Limit SeqID="8" Lat="57.6629480" Lon="11.8249660"/>
|
||||
<Limit SeqID="9" Lat="57.6686890" Lon="11.8250920"/>
|
||||
<Limit SeqID="10" Lat="57.6708220" Lon="11.8321340"/>
|
||||
</CourseLimit>
|
||||
</Race>
|
||||
@@ -4,62 +4,39 @@
|
||||
<RaceStartTime Start="${raceStartTime}" Postpone="False" />
|
||||
<RaceID>15082901</RaceID>
|
||||
<RaceType>Fleet</RaceType>
|
||||
|
||||
<Participants>
|
||||
<#list boats as boat>
|
||||
<Yacht SourceID="${boat.sourceId}"/>
|
||||
</#list>
|
||||
</Participants>
|
||||
|
||||
<Tokens>
|
||||
<#list tokens as token>
|
||||
<Token TokenType="${token.tokenType}" TargetLat="${token.lat?c}" TargetLng="${token.lng?c}"/>
|
||||
</#list>
|
||||
</Tokens>
|
||||
|
||||
<Course>
|
||||
<CompoundMark CompoundMarkID="1" Name="Mark0">
|
||||
<Mark SeqID="1" Name="Start Line 1" TargetLat="57.670603" TargetLng="11.828262" SourceID="122" />
|
||||
<Mark SeqID="2" Name="Start Line 2" TargetLat="57.669445" TargetLng="11.826413" SourceID="123" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="2" Name="Mark1">
|
||||
<Mark SeqID="1" Name="Mark1" TargetLat="57.6675700" TargetLng="11.8359880" SourceID="131" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="3" Name="Mark2">
|
||||
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="124" />
|
||||
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.671629" TargetLng="11.840951" SourceID="125" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="4" Name="Mark3">
|
||||
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.664190" TargetLng="11.829576" SourceID="126" />
|
||||
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.665316" TargetLng="11.827184" SourceID="127" />
|
||||
</CompoundMark>
|
||||
<CompoundMark CompoundMarkID="5" Name="Mark4">
|
||||
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.672350" TargetLng="11.842535" SourceID="128" />
|
||||
<Mark SeqID="2" Name="Finish Line 2" TargetLat="57.6715240" TargetLng="11.8444950" SourceID="129" />
|
||||
<#list compoundMarks as compoundMark>
|
||||
<CompoundMark CompoundMarkID="${compoundMark.id?c}" Name="${compoundMark.name}">
|
||||
<#list compoundMark.marks as mark>
|
||||
<Mark SeqID="${mark.seqID?c}" Name="${mark.name}" TargetLat="${mark.lat?c}" TargetLng="${mark.lng?c}" SourceID="${mark.sourceID?c}" />
|
||||
</#list>
|
||||
</CompoundMark>
|
||||
</#list>
|
||||
</Course>
|
||||
|
||||
<CompoundMarkSequence>
|
||||
<Corner SeqID="1" CompoundMarkID="1" Rounding="PS" ZoneSize="3" />
|
||||
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
|
||||
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
||||
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
||||
<Corner SeqID="5" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
||||
<Corner SeqID="6" CompoundMarkID="4" 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" />
|
||||
<#list roundings as corner>
|
||||
<Corner SeqID="${corner.seqID?c}" CompoundMarkID="${corner.compoundMarkID?c}" Rounding="${corner.rounding}" ZoneSize="${corner.zoneSize?c}" />
|
||||
</#list>
|
||||
</CompoundMarkSequence>
|
||||
|
||||
<CourseLimit>
|
||||
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100" />
|
||||
<Limit SeqID="2" Lat="57.6709520" Lon="11.8485010" />
|
||||
<Limit SeqID="3" Lat="57.6690260" Lon="11.8472790" />
|
||||
<Limit SeqID="4" Lat="57.6693140" Lon="11.8457610" />
|
||||
<Limit SeqID="5" Lat="57.6665370" Lon="11.8432910" />
|
||||
<Limit SeqID="6" Lat="57.6641400" Lon="11.8385840" />
|
||||
<Limit SeqID="7" Lat="57.6629430" Lon="11.8332030" />
|
||||
<Limit SeqID="8" Lat="57.6629480" Lon="11.8249660" />
|
||||
<Limit SeqID="9" Lat="57.6686890" Lon="11.8250920" />
|
||||
<Limit SeqID="10" Lat="57.6692230" Lon="11.8231430" />
|
||||
<Limit SeqID="11" Lat="57.6725370" Lon="11.8272480" />
|
||||
<Limit SeqID="12" Lat="57.6708220" Lon="11.8321340" />
|
||||
<#list courseLimit as limit>
|
||||
<Limit SeqID="${limit.seqID?c}" Lat="${limit.lat?c}" Lon="${limit.lng?c}" />
|
||||
</#list>
|
||||
</CourseLimit>
|
||||
|
||||
</Race>
|
||||
@@ -5,15 +5,15 @@
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<StackPane fx:id="serverListMainStackPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
|
||||
<StackPane fx:id="serverListMainStackPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.LobbyController">
|
||||
<children>
|
||||
<GridPane fx:id="serverListMainGridPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
|
||||
<children>
|
||||
@@ -86,11 +86,14 @@
|
||||
<Insets bottom="15.0" left="7.0" right="15.0" top="15.0" />
|
||||
</GridPane.margin>
|
||||
</ScrollPane>
|
||||
<Pane fx:id="serverMap" style="-fx-background-color: skyblue;">
|
||||
<AnchorPane fx:id="serverMap" style="-fx-background-color: skyblue;">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="15.0" left="15.0" right="7.0" top="15.0" />
|
||||
</GridPane.margin>
|
||||
</Pane>
|
||||
</AnchorPane>
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
|
||||
@@ -305,4 +305,4 @@
|
||||
<String fx:value="/css/Master.css"/>
|
||||
<String fx:value="/css/RaceView.css"/>
|
||||
</stylesheets>
|
||||
</AnchorPane>
|
||||
</StackPane>
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXDialogLayout?>
|
||||
<?import com.jfoenix.controls.JFXToggleButton?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<JFXDialogLayout fx:id="keyBindDialog" maxHeight="-Infinity" maxWidth="-Infinity"
|
||||
minHeight="-Infinity" minWidth="-Infinity" prefHeight="580.0" prefWidth="500.0"
|
||||
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="seng302.visualiser.controllers.dialogs.KeyBindingDialogController">
|
||||
<children>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="70.0"
|
||||
vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label fx:id="keyBindingDialogHeader" text="CUSTOM KEYBIND" GridPane.columnSpan="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
|
||||
<Label text="ZOOM IN" GridPane.halignment="CENTER" GridPane.rowIndex="1"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label text="ZOOM OUT" GridPane.halignment="CENTER" GridPane.rowIndex="2"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label text="VMG" GridPane.halignment="CENTER" GridPane.rowIndex="3"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label text="SAILS IN/OUT" GridPane.halignment="CENTER" GridPane.rowIndex="4"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label text="TACK/GYBE" GridPane.halignment="CENTER" GridPane.rowIndex="5"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label fx:id="upwindLabel" text="UPWIND" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="6" GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<Label fx:id="downwindLabel" text="DOWNWIND" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="7" GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin></Label>
|
||||
<JFXButton id="ZOOM IN" fx:id="zoomInbtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" text="Z"
|
||||
GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1"
|
||||
GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="ZOOM OUT" fx:id="zoomOutBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0" text="X"
|
||||
GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2"
|
||||
GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="VMG" fx:id="vmgBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0"
|
||||
text="SPACE" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="3" GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="SAILS IN/OUT" fx:id="sailInOutBtn" buttonType="RAISED"
|
||||
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity"
|
||||
prefWidth="120.0" text="SHIFT" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="4" GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="TACK/GYBE" fx:id="tackGybeBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0"
|
||||
text="ENTER" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="5" GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="UPWIND" fx:id="upwindBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0"
|
||||
text="PAGE_UP" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="6" GridPane.valignment="CENTER"/>
|
||||
<JFXButton id="DOWNWIND" fx:id="downwindBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="35.0" minWidth="-Infinity" prefWidth="120.0"
|
||||
text="PAGE_DOWN" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="7" GridPane.valignment="CENTER"/>
|
||||
<JFXToggleButton fx:id="turningToggle" minHeight="-Infinity" prefHeight="35.0"
|
||||
text="OFF / ON" GridPane.columnIndex="1" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="8"/>
|
||||
<Label text="CONTINUOUSLY TURNING" GridPane.halignment="CENTER" GridPane.rowIndex="8"
|
||||
GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<JFXButton fx:id="resetBtn" buttonType="RAISED" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="45.0"
|
||||
prefWidth="150.0" text="RESET" GridPane.columnSpan="2" GridPane.halignment="CENTER"
|
||||
GridPane.rowIndex="9" GridPane.valignment="CENTER"/>
|
||||
<Label fx:id="closeLabel" text="✖" translateY="-5.0" GridPane.columnIndex="1"
|
||||
GridPane.halignment="RIGHT" GridPane.valignment="TOP"/>
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
<stylesheets>
|
||||
<URL value="@../../css/dialogs/KeyBindingDialog.css" />
|
||||
<URL value="@../../css/Master.css" />
|
||||
</stylesheets>
|
||||
</JFXDialogLayout>
|
||||
@@ -1,49 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXCheckBox?>
|
||||
<?import com.jfoenix.controls.JFXDialogLayout?>
|
||||
<?import com.jfoenix.controls.JFXSlider?>
|
||||
<?import com.jfoenix.controls.JFXTextField?>
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<JFXDialogLayout maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="550.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.dialogs.ServerCreationController">
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<JFXDialogLayout maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.dialogs.ServerCreationController">
|
||||
<children>
|
||||
<GridPane>
|
||||
<children>
|
||||
<JFXButton fx:id="submitBtn" onMouseEntered="#playButtonHoverSound" prefHeight="55.0" prefWidth="250.0" text="SUBMIT" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font></JFXButton>
|
||||
<GridPane GridPane.rowIndex="1">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="90.0" minHeight="90.0" prefHeight="90.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="100.0" minHeight="100.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="100.0" minHeight="100.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="100.0" minHeight="100.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label fx:id="hostDialogHeader" text="HOST A GAME SERVER" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
|
||||
<JFXButton fx:id="submitBtn" onMouseEntered="#playButtonHoverSound" prefHeight="55.0" prefWidth="250.0" text="SUBMIT" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
|
||||
<JFXTextField fx:id="serverName" promptText="SERVER NAME" GridPane.rowIndex="1">
|
||||
<JFXTextField fx:id="serverName" promptText="SERVER NAME">
|
||||
<padding>
|
||||
<Insets left="15.0" right="15.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets left="30.0" right="30.0" />
|
||||
</GridPane.margin></JFXTextField>
|
||||
<GridPane fx:id="maxPlayersGridPane" GridPane.rowIndex="2">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" percentHeight="60.0" prefHeight="10.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" percentHeight="40.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<Insets left="35.0" right="35.0" />
|
||||
</GridPane.margin>
|
||||
</JFXTextField>
|
||||
<GridPane fx:id="maxPlayersGridPane" GridPane.rowIndex="1">
|
||||
<children>
|
||||
<Label fx:id="maxPlayersLabel" text="20" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets right="30.0" />
|
||||
</GridPane.margin></Label>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<Label fx:id="maxPlayerPromptLabel" text="MAX PLAYERS" GridPane.halignment="LEFT" GridPane.valignment="BOTTOM">
|
||||
<GridPane.margin>
|
||||
<Insets left="30.0" top="20.0" />
|
||||
@@ -58,12 +70,109 @@
|
||||
</GridPane.margin>
|
||||
</VBox>
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" percentHeight="60.0" prefHeight="10.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" percentHeight="40.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
<GridPane fx:id="maxPlayersGridPane1" GridPane.rowIndex="2">
|
||||
<children>
|
||||
<Label fx:id="legsSliderLabel" text="20" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets right="30.0" />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<VBox alignment="BOTTOM_CENTER" prefHeight="200.0" prefWidth="100.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||
<children>
|
||||
<JFXSlider fx:id="legsSlider" blockIncrement="1.0" majorTickUnit="2.0" max="20.0" min="1.0" minorTickCount="1" snapToTicks="true" styleClass="maxPlayers" value="11.0" />
|
||||
</children>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" right="30.0" />
|
||||
</GridPane.margin>
|
||||
</VBox>
|
||||
<GridPane>
|
||||
<children>
|
||||
<Label alignment="CENTER" text="NUMBER OF" GridPane.halignment="CENTER" />
|
||||
<Label alignment="CENTER" text="REPEATING LEGS" GridPane.halignment="CENTER" GridPane.rowIndex="1" />
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
<JFXCheckBox fx:id="pickupsCheckBox" text="Enable Pickups" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets left="5.0" />
|
||||
</GridPane.margin>
|
||||
</JFXCheckBox>
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" percentHeight="60.0" prefHeight="10.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" percentHeight="40.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
</children>
|
||||
</GridPane>
|
||||
<GridPane GridPane.columnIndex="1">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="35.0" prefWidth="35.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="35.0" minWidth="35.0" prefWidth="35.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<JFXButton fx:id="lastMapButton" maxHeight="300.0" minHeight="300.0" prefHeight="300.0" prefWidth="35.0" style="-fx-border-color: lightgrey; -fx-border-radius: 5;" text="<" textFill="#5b5b5b" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font></JFXButton>
|
||||
<JFXButton fx:id="nextMapButton" maxHeight="300.0" minHeight="300.0" minWidth="35.0" prefHeight="300.0" style="-fx-border-color: lightgrey; -fx-border-radius: 5;" text=">" textFill="#5b5b5b" GridPane.columnIndex="2" GridPane.valignment="CENTER">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font></JFXButton>
|
||||
<GridPane style="-fx-background-color: lightblue; -fx-border-color: grey; -fx-border-radius: 5; -fx-background-radius: 5;" GridPane.columnIndex="1">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="25.0" minHeight="25.0" prefHeight="25.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="342.0" minHeight="10.0" prefHeight="336.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label fx:id="mapNameLabel" text="MAP NAME" GridPane.halignment="CENTER" />
|
||||
<AnchorPane fx:id="mapHolder" prefHeight="333.0" prefWidth="404.0" GridPane.rowIndex="1" />
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
</GridPane>
|
||||
<Label fx:id="hostDialogHeader" text="HOST A GAME SERVER" GridPane.halignment="CENTER">
|
||||
<font>
|
||||
<Font size="20.0" />
|
||||
</font></Label>
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="100.0" minHeight="100.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="511.0" minHeight="90.0" prefHeight="370.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="102.0" minHeight="100.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
</children>
|
||||
<stylesheets>
|
||||
<String fx:value="/css/dialogs/ServerCreation.css" />
|
||||
<String fx:value="/css/Master.css"/>
|
||||
</stylesheets>
|
||||
</JFXDialogLayout>
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ChatCommandsTest {
|
||||
}
|
||||
try {
|
||||
dcSent = false;
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
mst = new MainServerThread();
|
||||
host = new ClientToServerThread("localhost", 4942);
|
||||
host.addStreamObserver(() -> {
|
||||
@@ -85,7 +85,7 @@ public class ChatCommandsTest {
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
mst = new MainServerThread();
|
||||
host = null;
|
||||
try {
|
||||
@@ -129,7 +129,7 @@ public class ChatCommandsTest {
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
mst = new MainServerThread();
|
||||
host = null;
|
||||
try {
|
||||
@@ -212,7 +212,7 @@ public class ChatCommandsTest {
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
dcSent = false;
|
||||
mst = new MainServerThread();
|
||||
host = null;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package seng302.model;
|
||||
|
||||
import static seng302.gameServer.GameState.checkCollision;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -7,8 +9,6 @@ import seng302.gameServer.GameState;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
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
|
||||
* meters.
|
||||
@@ -24,10 +24,10 @@ public class UpdateYachtTest {
|
||||
|
||||
@Before
|
||||
public void setUpRace() {
|
||||
new GameState("");
|
||||
new GameState();
|
||||
GameState.addYacht(1, yacht1);
|
||||
GameState.addYacht(2, yacht2);
|
||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/server_config/acc_polars.csv"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -7,8 +7,12 @@ import static junit.framework.TestCase.assertTrue;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.MarkOrder;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.utilities.StreamParser;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
public class MarkOrderTest {
|
||||
private static MarkOrder markOrder;
|
||||
@@ -16,7 +20,32 @@ public class MarkOrderTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setup(){
|
||||
markOrder = new MarkOrder();
|
||||
/*
|
||||
%%%%%%
|
||||
%%%% = =
|
||||
%%C >
|
||||
_)' _( .' ,
|
||||
__/ |_/\ " *. o
|
||||
/` \_\ \/ %`= '_ .
|
||||
/ ) \/| .^',*. ,
|
||||
/' /- o/ - " % '_
|
||||
/\_/ < = , ^ ~ .
|
||||
)_o|----'| .` '
|
||||
___// (_ - (\
|
||||
///-( \' \\
|
||||
|
||||
*/
|
||||
markOrder = new MarkOrder(
|
||||
XMLParser.parseRace(
|
||||
StreamParser.extractXmlMessage(
|
||||
new StreamPacket(
|
||||
XMLMessageSubType.RACE.getType(),
|
||||
XMLParser.parseRaceDef("/maps/default.xml", "test", 2).getValue().length(),
|
||||
0, XMLParser.parseRaceDef("/maps/default.xml", "test", 2).getValue().getBytes()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
currentSeqID = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.junit.BeforeClass;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
import seng302.visualiser.fxObjects.assets_3D.BoatMeshType;
|
||||
|
||||
|
||||
public class YachtTest {
|
||||
@@ -17,9 +18,9 @@ public class YachtTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
y1 = new ServerYacht(BoatMeshType.DINGHY, 1, "Y1", "Y1", "Yacht 1", "C1");
|
||||
gs = new GameState("localhost");
|
||||
gs = new GameState();
|
||||
}
|
||||
|
||||
//Commented out until can fix the weird non-deterministic bug.
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package seng302.visualiser.ClientToServerTests;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import seng302.gameServer.GameStages;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.MainServerThread;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
|
||||
/**
|
||||
@@ -22,7 +18,7 @@ public class RegularPacketsTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
new GameState("localhost");
|
||||
new GameState();
|
||||
serverThread = new MainServerThread();
|
||||
clientThread = new ClientToServerThread("localhost", 4942);
|
||||
GameState.setCurrentStage(GameStages.RACING);
|
||||
|
||||