|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
package visualiser.Controllers;
|
|
|
|
|
|
|
|
|
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
|
|
|
|
import eu.hansolo.medusa.*;
|
|
|
|
|
import javafx.animation.AnimationTimer;
|
|
|
|
|
import javafx.application.Platform;
|
|
|
|
|
import javafx.collections.FXCollections;
|
|
|
|
|
@ -12,20 +13,29 @@ import javafx.scene.AmbientLight;
|
|
|
|
|
import javafx.scene.PointLight;
|
|
|
|
|
import javafx.scene.chart.LineChart;
|
|
|
|
|
import javafx.scene.control.*;
|
|
|
|
|
import javafx.scene.image.Image;
|
|
|
|
|
import javafx.scene.image.ImageView;
|
|
|
|
|
import javafx.scene.input.KeyCode;
|
|
|
|
|
import javafx.scene.input.KeyEvent;
|
|
|
|
|
import javafx.scene.layout.AnchorPane;
|
|
|
|
|
import javafx.scene.layout.GridPane;
|
|
|
|
|
import javafx.scene.layout.Pane;
|
|
|
|
|
import javafx.scene.layout.StackPane;
|
|
|
|
|
import javafx.scene.media.AudioClip;
|
|
|
|
|
import javafx.scene.paint.Color;
|
|
|
|
|
import javafx.scene.paint.Material;
|
|
|
|
|
import javafx.scene.paint.PhongMaterial;
|
|
|
|
|
import javafx.scene.shape.Cylinder;
|
|
|
|
|
import javafx.scene.paint.Stop;
|
|
|
|
|
import javafx.scene.shape.MeshView;
|
|
|
|
|
import javafx.scene.shape.Shape3D;
|
|
|
|
|
import javafx.scene.shape.Sphere;
|
|
|
|
|
import javafx.scene.transform.Translate;
|
|
|
|
|
import javafx.util.Callback;
|
|
|
|
|
import network.Messages.Enums.BoatStatusEnum;
|
|
|
|
|
import network.Messages.Enums.RaceStatusEnum;
|
|
|
|
|
import shared.dataInput.RaceDataSource;
|
|
|
|
|
import shared.enums.RoundingType;
|
|
|
|
|
import shared.exceptions.BoatNotFoundException;
|
|
|
|
|
import shared.model.*;
|
|
|
|
|
import visualiser.app.App;
|
|
|
|
|
@ -38,11 +48,9 @@ import visualiser.model.*;
|
|
|
|
|
import visualiser.utils.GPSConverter;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.net.URL;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.logging.Level;
|
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
|
|
@ -61,9 +69,17 @@ public class RaceViewController extends Controller {
|
|
|
|
|
private boolean isTutorial = false;
|
|
|
|
|
private String keyToPress;
|
|
|
|
|
private View3D view3D;
|
|
|
|
|
private WindCompass windCompass;
|
|
|
|
|
private ObservableList<Subject3D> viewSubjects;
|
|
|
|
|
private Gauge gauge;
|
|
|
|
|
private FGauge fGauge;
|
|
|
|
|
private long positionDelay = 1000;
|
|
|
|
|
private long positionTime = 0;
|
|
|
|
|
private ResizableRaceCanvas raceCanvas;
|
|
|
|
|
private boolean mapToggle = true;
|
|
|
|
|
private GPSConverter gpsConverter;
|
|
|
|
|
private ArrayList<HealthEffect> healthEffectList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Arrow pointing to next mark in third person
|
|
|
|
|
@ -74,12 +90,22 @@ public class RaceViewController extends Controller {
|
|
|
|
|
*/
|
|
|
|
|
private AnimationTimer pointToMark;
|
|
|
|
|
|
|
|
|
|
//seagulls
|
|
|
|
|
private ObservableList<Subject3D> seagulls = FXCollections.observableArrayList();
|
|
|
|
|
//seagulls goto
|
|
|
|
|
private Map<Subject3D, List<Double>> seagullsGoToX = new HashMap<>();
|
|
|
|
|
private Map<Subject3D, List<Double>> seagullsGoToY = new HashMap<>();
|
|
|
|
|
private double seagullSpeed = 0.01;
|
|
|
|
|
|
|
|
|
|
// note: it says it's not used but it is! do not remove :)
|
|
|
|
|
private @FXML ArrowController arrowController;
|
|
|
|
|
private @FXML NextMarkController nextMarkController;
|
|
|
|
|
private @FXML GridPane canvasBase;
|
|
|
|
|
private @FXML GridPane canvasBase1;
|
|
|
|
|
private @FXML GridPane canvasBase2;
|
|
|
|
|
private @FXML SplitPane racePane;
|
|
|
|
|
private @FXML StackPane arrowPane;
|
|
|
|
|
private @FXML Pane nextMarkPane;
|
|
|
|
|
private @FXML Label timer;
|
|
|
|
|
private @FXML Label FPS;
|
|
|
|
|
private @FXML Label timeZone;
|
|
|
|
|
@ -89,10 +115,17 @@ public class RaceViewController extends Controller {
|
|
|
|
|
private @FXML TableColumn<VisualiserBoat, String> boatTeamColumn;
|
|
|
|
|
private @FXML TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
|
|
|
|
private @FXML TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
|
|
|
|
private @FXML TableColumn<VisualiserBoat, Number> boatHealthColumn;
|
|
|
|
|
private @FXML LineChart<Number, Number> sparklineChart;
|
|
|
|
|
private @FXML Label tutorialText;
|
|
|
|
|
private @FXML AnchorPane infoWrapper;
|
|
|
|
|
private @FXML AnchorPane lineChartWrapper;
|
|
|
|
|
private @FXML StackPane speedPane;
|
|
|
|
|
private @FXML AnchorPane raceAnchorPane;
|
|
|
|
|
private @FXML GridPane playerHealthContainer;
|
|
|
|
|
private @FXML ImageView imageView;
|
|
|
|
|
private @FXML AnchorPane deathTransPane;
|
|
|
|
|
private @FXML StackPane deathPane;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Displays a specified race.
|
|
|
|
|
@ -107,11 +140,13 @@ public class RaceViewController extends Controller {
|
|
|
|
|
this.controllerClient = controllerClient;
|
|
|
|
|
this.isHost = isHost;
|
|
|
|
|
keyFactory.load();
|
|
|
|
|
|
|
|
|
|
deathPane.setDisable(false);
|
|
|
|
|
deathPane.setVisible(false);
|
|
|
|
|
tutorialCheck();
|
|
|
|
|
initKeypressHandler();
|
|
|
|
|
healthLoop();
|
|
|
|
|
initialiseRaceVisuals();
|
|
|
|
|
initialiseRaceCanvas();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -123,7 +158,6 @@ public class RaceViewController extends Controller {
|
|
|
|
|
isTutorial = true;
|
|
|
|
|
tutorialText.setVisible(true);
|
|
|
|
|
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
|
|
|
|
|
|
|
|
|
|
currentState = tutorialStates.get(0);
|
|
|
|
|
tutorialStates.remove(0);
|
|
|
|
|
searchMapForKey("Upwind");
|
|
|
|
|
@ -205,36 +239,121 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialises the various UI components to listen to the {@link #visualiserRace}.
|
|
|
|
|
* Create speedometer
|
|
|
|
|
*/
|
|
|
|
|
private void initialiseRaceVisuals() {
|
|
|
|
|
|
|
|
|
|
// Import arrow mesh
|
|
|
|
|
URL asset = this.getClass().getClassLoader().getResource("assets/arrow V1.0.4.stl");
|
|
|
|
|
StlMeshImporter importer = new StlMeshImporter();
|
|
|
|
|
importer.read(asset);
|
|
|
|
|
private void initialiseSpeedometer() {
|
|
|
|
|
//Create the Medusa Gauge
|
|
|
|
|
gauge = GaugeBuilder.create()
|
|
|
|
|
.prefSize(200,200)
|
|
|
|
|
.foregroundBaseColor(Color.WHITE)
|
|
|
|
|
.title("Title")
|
|
|
|
|
.subTitle("Speed")
|
|
|
|
|
.unit("Knots")
|
|
|
|
|
.decimals(2)
|
|
|
|
|
.lcdVisible(true)
|
|
|
|
|
.lcdDesign(LcdDesign.STANDARD)
|
|
|
|
|
.lcdFont(LcdFont.DIGITAL_BOLD)
|
|
|
|
|
.scaleDirection(Gauge.ScaleDirection.CLOCKWISE)
|
|
|
|
|
.minValue(0)
|
|
|
|
|
.maxValue(50)
|
|
|
|
|
.startAngle(320)
|
|
|
|
|
.angleRange(280)
|
|
|
|
|
.tickLabelDecimals(0)
|
|
|
|
|
.tickLabelLocation(TickLabelLocation.INSIDE)
|
|
|
|
|
.tickLabelOrientation(TickLabelOrientation.ORTHOGONAL)
|
|
|
|
|
.onlyFirstAndLastTickLabelVisible(false)
|
|
|
|
|
.tickLabelSectionsVisible(false)
|
|
|
|
|
.tickLabelColor(Color.BLACK)
|
|
|
|
|
.tickMarkSectionsVisible(false)
|
|
|
|
|
.majorTickMarksVisible(true)
|
|
|
|
|
.majorTickMarkType(TickMarkType.TRAPEZOID)
|
|
|
|
|
.mediumTickMarksVisible(false)
|
|
|
|
|
.mediumTickMarkType(TickMarkType.LINE)
|
|
|
|
|
.minorTickMarksVisible(true)
|
|
|
|
|
.minorTickMarkType(TickMarkType.LINE)
|
|
|
|
|
.ledVisible(false)
|
|
|
|
|
.ledType(Gauge.LedType.STANDARD)
|
|
|
|
|
.ledColor(Color.rgb(255, 200, 0))
|
|
|
|
|
.ledBlinking(false)
|
|
|
|
|
.needleShape(Gauge.NeedleShape.ANGLED)
|
|
|
|
|
.needleSize(Gauge.NeedleSize.STANDARD)
|
|
|
|
|
.needleColor(Color.CRIMSON)
|
|
|
|
|
.startFromZero(false)
|
|
|
|
|
.returnToZero(false)
|
|
|
|
|
.knobType(Gauge.KnobType.METAL)
|
|
|
|
|
.knobColor(Color.LIGHTGRAY)
|
|
|
|
|
.interactive(false)
|
|
|
|
|
.thresholdVisible(true)
|
|
|
|
|
.threshold(50)
|
|
|
|
|
.thresholdColor(Color.RED)
|
|
|
|
|
.checkThreshold(true)
|
|
|
|
|
.gradientBarEnabled(true)
|
|
|
|
|
.gradientBarStops(new Stop(0.0, Color.BLUE),
|
|
|
|
|
new Stop(0.25, Color.CYAN),
|
|
|
|
|
new Stop(0.5, Color.LIME),
|
|
|
|
|
new Stop(0.75, Color.YELLOW),
|
|
|
|
|
new Stop(1.0, Color.RED))
|
|
|
|
|
.markersVisible(true)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Create a gauge with a frame and background that utilizes a Medusa gauge
|
|
|
|
|
fGauge = FGaugeBuilder
|
|
|
|
|
.create()
|
|
|
|
|
.prefSize(190, 190)
|
|
|
|
|
.gauge(gauge)
|
|
|
|
|
.gaugeDesign(GaugeDesign.METAL)
|
|
|
|
|
.gaugeBackground(GaugeDesign.GaugeBackground.CARBON)
|
|
|
|
|
.foregroundVisible(true)
|
|
|
|
|
.build();
|
|
|
|
|
speedPane.getChildren().add(fGauge);
|
|
|
|
|
|
|
|
|
|
MeshView arrow = new MeshView(importer.getImport());
|
|
|
|
|
PhongMaterial arrowMat = new PhongMaterial(Color.RED);
|
|
|
|
|
arrow.setMaterial(arrowMat);
|
|
|
|
|
|
|
|
|
|
this.nextMarkArrow = new Annotation3D(arrow);
|
|
|
|
|
this.nextMarkArrow.setScale(0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialises the various UI components to listen to the {@link #visualiserRace}.
|
|
|
|
|
*/
|
|
|
|
|
private void initialiseRaceVisuals() {
|
|
|
|
|
// initialise displays
|
|
|
|
|
initialiseFps();
|
|
|
|
|
initialiseInfoTable();
|
|
|
|
|
initialiseView3D(this.visualiserRace);
|
|
|
|
|
initialiseHealthPane();
|
|
|
|
|
initialiseRaceClock();
|
|
|
|
|
initialiseSpeedometer();
|
|
|
|
|
initialiseRaceCanvas();
|
|
|
|
|
raceTimer(); // start the timer
|
|
|
|
|
//nextMarkPane.toFront();
|
|
|
|
|
speedometerLoop();
|
|
|
|
|
new Sparkline(this.raceState, this.sparklineChart);
|
|
|
|
|
timeZone.setText(this.raceState.getRaceClock().getTimeZone());
|
|
|
|
|
arrowController.setWindProperty(this.raceState.windProperty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void initialiseHealthPane() {
|
|
|
|
|
InputStream tomato = this.getClass().getClassLoader().getResourceAsStream("visualiser/images/tomato.png");
|
|
|
|
|
HealthSlider healthSlider = new HealthSlider(new Image(tomato));
|
|
|
|
|
playerHealthContainer.add(healthSlider, 0, 0);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
VisualiserBoat player = raceState.getBoat(raceState.getPlayerBoatID());
|
|
|
|
|
player.healthProperty().addListener((o, prev, curr) -> {
|
|
|
|
|
healthSlider.setCrop((double)curr/100.0);
|
|
|
|
|
});
|
|
|
|
|
} catch (BoatNotFoundException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initialiseView3D(VisualiserRaceEvent race) {
|
|
|
|
|
viewSubjects = FXCollections.observableArrayList();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
nextMarkController.initialiseArrowView(race.getVisualiserRaceState().getBoat(race.getVisualiserRaceState().getPlayerBoatID()));
|
|
|
|
|
} catch (BoatNotFoundException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
|
|
|
|
|
ambientLight.setTranslateX(250);
|
|
|
|
|
ambientLight.setTranslateZ(210);
|
|
|
|
|
@ -268,24 +387,46 @@ public class RaceViewController extends Controller {
|
|
|
|
|
view3D.addPointLight(pointLight);
|
|
|
|
|
canvasBase.add(view3D, 0, 0);
|
|
|
|
|
|
|
|
|
|
windCompass = new WindCompass(view3D, this.raceState.windProperty());
|
|
|
|
|
arrowPane.getChildren().add(windCompass);
|
|
|
|
|
|
|
|
|
|
// Set up projection from GPS to view
|
|
|
|
|
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
|
|
|
|
final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450);
|
|
|
|
|
gpsConverter = new GPSConverter(raceData, 450, 450);
|
|
|
|
|
|
|
|
|
|
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
|
|
|
|
|
SkyBox skyBox = Assets3D.skyBox;
|
|
|
|
|
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
|
|
|
|
|
|
|
|
|
|
// Set up sea surface
|
|
|
|
|
SeaSurface sea = new SeaSurface(750, 200);
|
|
|
|
|
sea.setX(250);
|
|
|
|
|
sea.setZ(210);
|
|
|
|
|
viewSubjects.add(sea);
|
|
|
|
|
// SeaSurface sea = new SeaSurface(750, 200);
|
|
|
|
|
// sea.setX(250);
|
|
|
|
|
// sea.setZ(210);
|
|
|
|
|
// viewSubjects.add(sea);
|
|
|
|
|
|
|
|
|
|
// Set up sea surface overlay
|
|
|
|
|
SeaSurface seaOverlay = new SeaSurface(4000, 200);
|
|
|
|
|
seaOverlay.setX(250);
|
|
|
|
|
seaOverlay.setZ(210);
|
|
|
|
|
viewSubjects.add(seaOverlay);
|
|
|
|
|
// SeaSurface seaOverlay = new SeaSurface(4000, 200);
|
|
|
|
|
// seaOverlay.setX(250);
|
|
|
|
|
// seaOverlay.setZ(210);
|
|
|
|
|
// viewSubjects.add(seaOverlay);
|
|
|
|
|
// int seaX = 15;
|
|
|
|
|
// int seaY = 15;
|
|
|
|
|
int seaX = 15;
|
|
|
|
|
int seaY = 15;
|
|
|
|
|
for (int x = 0; x < seaX; x++) {
|
|
|
|
|
for (int y = 0; y < seaY; y++) {
|
|
|
|
|
NewSeaSurface seaSurface = new NewSeaSurface();
|
|
|
|
|
Subject3D seaSubject = new Annotation3D(seaSurface);
|
|
|
|
|
seaSubject.setXRot(0);
|
|
|
|
|
// seaSubject.setX(-75 + x * 50);
|
|
|
|
|
// seaSubject.setZ(-150 + y * 50);
|
|
|
|
|
// seaSubject.setX(-150 + x * 250);
|
|
|
|
|
// seaSubject.setZ(-80 + y * 250);
|
|
|
|
|
seaSubject.setX(-75 + x * 100);
|
|
|
|
|
seaSubject.setZ(-150 + y * 100);
|
|
|
|
|
seaSubject.setY(3);
|
|
|
|
|
viewSubjects.add(seaSubject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
|
|
|
|
|
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
|
|
|
|
|
@ -293,8 +434,9 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}
|
|
|
|
|
// Position and add each mark to view
|
|
|
|
|
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
|
|
|
|
|
MeshView mesh = new MeshView(importerMark.getImport());
|
|
|
|
|
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
|
|
|
|
|
// MeshView mesh = new MeshView(importerMark.getImport());
|
|
|
|
|
// Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
|
|
|
|
|
Subject3D markModel = new Subject3D(Assets3D.getMark(), mark.getSourceID());
|
|
|
|
|
|
|
|
|
|
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
|
|
|
|
|
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
|
|
|
|
|
@ -302,35 +444,158 @@ public class RaceViewController extends Controller {
|
|
|
|
|
viewSubjects.add(markModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
|
|
|
|
// Position and add each boat to view
|
|
|
|
|
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
|
|
|
|
Shape3D mesh = Assets3D.getBoat();
|
|
|
|
|
|
|
|
|
|
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
|
|
|
|
//mesh.setMaterial(boatColorMat);
|
|
|
|
|
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
|
|
|
|
|
|
|
|
|
// Configure visualiser for client's boat
|
|
|
|
|
if (boat.isClientBoat()) {
|
|
|
|
|
Shockwave boatHighlight = new Shockwave(10);
|
|
|
|
|
boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
|
|
|
|
viewSubjects.add(boatHighlight);
|
|
|
|
|
// Add player boat highlight
|
|
|
|
|
// Shockwave boatHighlight = new Shockwave(10);
|
|
|
|
|
// boatHighlight.getMesh().setMaterial(new PhongMaterial(new Color(1, 1, 0, 0.1)));
|
|
|
|
|
// viewSubjects.add(boatHighlight);
|
|
|
|
|
|
|
|
|
|
viewSubjects.add(Assets3D.boatHighlight);
|
|
|
|
|
|
|
|
|
|
// Track player boat with camera
|
|
|
|
|
viewSubjects.add(boatModel);
|
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
|
view3D.trackSubject(boatModel, 0);
|
|
|
|
|
view3D.setThirdPerson();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Track player boat with highlight
|
|
|
|
|
AnimationTimer highlightTrack = new AnimationTimer() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
boatHighlight.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
boatHighlight.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
Assets3D.boatHighlight.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
Assets3D.boatHighlight.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
highlightTrack.start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Position and add each boat to view
|
|
|
|
|
for (VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
|
|
|
|
|
MeshView mesh;
|
|
|
|
|
if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
|
|
|
|
|
mesh = new MeshView(importer.getImport());
|
|
|
|
|
// Highlight next mark only for player boat
|
|
|
|
|
// Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
|
|
|
|
// CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
|
|
|
|
// view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor);
|
|
|
|
|
// if(nextMark.getMark2() != null) {
|
|
|
|
|
// view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor);
|
|
|
|
|
// }
|
|
|
|
|
// boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
|
|
|
|
|
|
|
|
|
//next mark indicator
|
|
|
|
|
changeNextMark(boat.getCurrentLeg());
|
|
|
|
|
viewSubjects.add(Assets3D.ccwNextArrow);
|
|
|
|
|
viewSubjects.add(Assets3D.cwNextArrow);
|
|
|
|
|
boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> changeNextMark(curr)));
|
|
|
|
|
} else {
|
|
|
|
|
mesh = new MeshView(importerBurgerBoat.getImport());
|
|
|
|
|
viewSubjects.add(boatModel);
|
|
|
|
|
boatModel.getMesh().toFront();
|
|
|
|
|
}
|
|
|
|
|
PhongMaterial boatColorMat = new PhongMaterial(boat.getColor());
|
|
|
|
|
mesh.setMaterial(boatColorMat);
|
|
|
|
|
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
|
|
|
|
|
|
|
|
|
|
viewSubjects.add(boatModel);
|
|
|
|
|
//Create health effect
|
|
|
|
|
HealthEffect healthEffect = new HealthEffect(boat.getSourceID(), System.currentTimeMillis());
|
|
|
|
|
viewSubjects.add(healthEffect);
|
|
|
|
|
healthEffectList.add(healthEffect);
|
|
|
|
|
|
|
|
|
|
//add sail
|
|
|
|
|
Sails3D sails3D = new Sails3D();
|
|
|
|
|
Subject3D sailsSubject = new Subject3D(sails3D, 0);
|
|
|
|
|
sails3D.setMouseTransparent(true);
|
|
|
|
|
sails3D.setMaterial(boatColorMat);
|
|
|
|
|
sailsSubject.setXRot(0d);
|
|
|
|
|
sailsSubject.setHeading(visualiserRace.getVisualiserRaceState().getWindDirection().degrees());
|
|
|
|
|
viewSubjects.add(sailsSubject);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SeagullFlock seagullFlock = new SeagullFlock(67, 43, 0);
|
|
|
|
|
//// seagullFlock.addToFlock();
|
|
|
|
|
//// seagullFlock.addToFlock();
|
|
|
|
|
// seagullFlock.setxBound(450);
|
|
|
|
|
// seagullFlock.setyBound(450);
|
|
|
|
|
// viewSubjects.addAll(seagullFlock.getSeagulls());
|
|
|
|
|
//
|
|
|
|
|
// Subject3D textSubject = new Annotation3D(new SeagullMesh());
|
|
|
|
|
// textSubject.setY(-10);
|
|
|
|
|
// viewSubjects.add(textSubject);
|
|
|
|
|
addToFlock();
|
|
|
|
|
seagullGoTo.start();
|
|
|
|
|
|
|
|
|
|
AnimationTimer rotateArrows = new AnimationTimer() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
Assets3D.ccwNextArrow.setHeading(Assets3D.ccwNextArrow.getHeading().getAngle() - 3.0);
|
|
|
|
|
Assets3D.cwNextArrow.setHeading(Assets3D.cwNextArrow.getHeading().getAngle() + 3.0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
rotateArrows.start();
|
|
|
|
|
|
|
|
|
|
AnimationTimer sailsFollowBoat = new AnimationTimer() {
|
|
|
|
|
double sailCurrent = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
|
|
|
|
boolean canLuff = true;
|
|
|
|
|
double turnRate = 5;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
double sailDir;
|
|
|
|
|
//if sails are out etc
|
|
|
|
|
if (boat.isSailsOut()) {
|
|
|
|
|
double windDir = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
|
|
|
|
double windOffset = (360 - windDir + boat.getBearing().degrees()) % 360;
|
|
|
|
|
sailDir = windOffset / 180 * 270 + windDir + 180;
|
|
|
|
|
boolean leftOfWind = windOffset >= 180;
|
|
|
|
|
if (leftOfWind){
|
|
|
|
|
sailDir = -sailDir;
|
|
|
|
|
} else {
|
|
|
|
|
sailDir = windDir - sailDir;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sailDir = visualiserRace.getVisualiserRaceState().getWindDirection().degrees();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//get new place to move towards
|
|
|
|
|
double compA = ((sailCurrent - sailDir) % 360 + 360) % 360;//degrees right
|
|
|
|
|
if (compA > 180) compA = 360 - compA;
|
|
|
|
|
double compB = ((sailDir - sailCurrent) % 360 + 360) % 360;//degrees left
|
|
|
|
|
if (compB > compA){
|
|
|
|
|
if (compA > turnRate){
|
|
|
|
|
sailCurrent = ((sailCurrent - turnRate) % 360 + 360) % 360;
|
|
|
|
|
canLuff = false;
|
|
|
|
|
} else {
|
|
|
|
|
sailCurrent = sailDir;
|
|
|
|
|
canLuff = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (compB > turnRate){
|
|
|
|
|
sailCurrent = ((sailCurrent + turnRate) % 360 + 360) % 360;
|
|
|
|
|
canLuff = false;
|
|
|
|
|
} else {
|
|
|
|
|
sailCurrent = sailDir;
|
|
|
|
|
canLuff = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sailsSubject.setHeading(sailCurrent);
|
|
|
|
|
if (canLuff) {
|
|
|
|
|
if (boat.isSailsOut()) {
|
|
|
|
|
if (sails3D.isLuffing()) {
|
|
|
|
|
sails3D.stopLuffing();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!sails3D.isLuffing()) {
|
|
|
|
|
sails3D.startLuffing();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sailsSubject.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
sailsSubject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
sailsSubject.getMesh().toFront();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
sailsFollowBoat.start();
|
|
|
|
|
|
|
|
|
|
// Track this boat's movement with the new subject
|
|
|
|
|
AnimationTimer trackBoat = new AnimationTimer() {
|
|
|
|
|
@ -339,21 +604,31 @@ public class RaceViewController extends Controller {
|
|
|
|
|
boatModel.setHeading(boat.getBearing().degrees());
|
|
|
|
|
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
boatModel.getMesh().toFront();
|
|
|
|
|
|
|
|
|
|
//Fire follows boat
|
|
|
|
|
healthEffect.setHeading(boat.getBearing().degrees());
|
|
|
|
|
healthEffect.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
healthEffect.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
healthEffect.setY(0);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
trackBoat.start();
|
|
|
|
|
|
|
|
|
|
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
|
|
|
|
CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
|
|
|
|
//next mark indicator
|
|
|
|
|
//Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
|
|
|
|
|
/*CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
|
|
|
|
|
|
|
|
|
|
view3D.getShape(nextMark.getMark1().getSourceID()).getMesh().setMaterial(markColor);
|
|
|
|
|
if(nextMark.getMark2() != null) {
|
|
|
|
|
view3D.getShape(nextMark.getMark2().getSourceID()).getMesh().setMaterial(markColor);
|
|
|
|
|
}
|
|
|
|
|
Subject3D shockwave = new Shockwave(10);
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
Subject3D shockwave = new Shockwave(10);
|
|
|
|
|
viewSubjects.add(shockwave);
|
|
|
|
|
|
|
|
|
|
boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
|
|
|
|
//boat.legProperty().addListener((o, prev, curr) -> Platform.runLater(() -> swapColours(curr)));
|
|
|
|
|
boat.hasCollidedProperty().addListener((o, prev, curr) -> Platform.runLater(() -> showCollision(boat, shockwave)));
|
|
|
|
|
}
|
|
|
|
|
// Fix initial bird's-eye position
|
|
|
|
|
@ -369,7 +644,12 @@ public class RaceViewController extends Controller {
|
|
|
|
|
|
|
|
|
|
// Bind zooming to scrolling
|
|
|
|
|
view3D.setOnScroll(e -> {
|
|
|
|
|
view3D.updateDistance(e.getDeltaY());
|
|
|
|
|
//view3D.updateDistance(e.getDeltaY());
|
|
|
|
|
if (e.getDeltaY() > 0) {
|
|
|
|
|
view3D.zoomIn();
|
|
|
|
|
} else {
|
|
|
|
|
view3D.zoomOut();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Bind zooming to keypress (Z/X default)
|
|
|
|
|
@ -390,7 +670,7 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
view3D.updateDistance(-10);
|
|
|
|
|
view3D.zoomIn();
|
|
|
|
|
break;
|
|
|
|
|
case "Zoom Out":
|
|
|
|
|
//Check if race is a tutorial
|
|
|
|
|
@ -405,7 +685,7 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
view3D.updateDistance(10);
|
|
|
|
|
view3D.zoomOut();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -438,44 +718,99 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addThirdPersonAnnotations(Subject3D subject3D) {
|
|
|
|
|
viewSubjects.add(nextMarkArrow);
|
|
|
|
|
final VisualiserBoat boat;
|
|
|
|
|
try {
|
|
|
|
|
boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
|
|
|
|
} catch (BoatNotFoundException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
arrowToNextMark = new AnimationTimer() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
|
|
|
|
if (target != null) {
|
|
|
|
|
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
|
|
|
|
|
|
|
|
|
nextMarkArrow.setX(view3D.getPivot().getX());
|
|
|
|
|
nextMarkArrow.setY(view3D.getPivot().getY());
|
|
|
|
|
nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
|
|
|
|
|
nextMarkArrow.setHeading(headingToMark.degrees());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
arrowToNextMark.start();
|
|
|
|
|
nextMarkController.show3d();
|
|
|
|
|
// viewSubjects.add(nextMarkArrow);
|
|
|
|
|
// final VisualiserBoat boat;
|
|
|
|
|
// try {
|
|
|
|
|
// boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
|
|
|
|
|
// } catch (BoatNotFoundException e) {
|
|
|
|
|
// e.printStackTrace();
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// arrowToNextMark = new AnimationTimer() {
|
|
|
|
|
// @Override
|
|
|
|
|
// public void handle(long now) {
|
|
|
|
|
// CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
|
|
|
|
|
// Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
|
|
|
|
|
//
|
|
|
|
|
// nextMarkArrow.setX(view3D.getPivot().getX());
|
|
|
|
|
// nextMarkArrow.setY(view3D.getPivot().getY());
|
|
|
|
|
// nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
|
|
|
|
|
// nextMarkArrow.setHeading(headingToMark.degrees());
|
|
|
|
|
// }
|
|
|
|
|
// };
|
|
|
|
|
// arrowToNextMark.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void removeThirdPersonAnnotations() {
|
|
|
|
|
viewSubjects.remove(nextMarkArrow);
|
|
|
|
|
if (arrowToNextMark != null) {
|
|
|
|
|
arrowToNextMark.stop();
|
|
|
|
|
nextMarkController.show2d();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setNextMarkArrowDirection(Mark mark1, Mark mark2, RoundingType roundingType, int direction){
|
|
|
|
|
boolean port = roundingType == RoundingType.Port;
|
|
|
|
|
Mark chosenMark = null;
|
|
|
|
|
|
|
|
|
|
if (mark2 == null){
|
|
|
|
|
chosenMark = mark1;
|
|
|
|
|
} else {
|
|
|
|
|
boolean rounding = port;
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case 2:
|
|
|
|
|
rounding = !port;
|
|
|
|
|
case 0:
|
|
|
|
|
if (rounding){
|
|
|
|
|
if (mark1.getPosition().getLongitude() < mark2.getPosition().getLongitude()){
|
|
|
|
|
chosenMark = mark1;
|
|
|
|
|
} else {
|
|
|
|
|
chosenMark = mark2;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (mark1.getPosition().getLongitude() > mark2.getPosition().getLongitude()){
|
|
|
|
|
chosenMark = mark1;
|
|
|
|
|
} else {
|
|
|
|
|
chosenMark = mark2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
rounding = !port;
|
|
|
|
|
case 1:
|
|
|
|
|
if (rounding){
|
|
|
|
|
if (mark1.getPosition().getLatitude() > mark2.getPosition().getLatitude()){
|
|
|
|
|
chosenMark = mark1;
|
|
|
|
|
} else {
|
|
|
|
|
chosenMark = mark2;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (mark1.getPosition().getLatitude() < mark2.getPosition().getLatitude()){
|
|
|
|
|
chosenMark = mark1;
|
|
|
|
|
} else {
|
|
|
|
|
chosenMark = mark2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chosenMark == null){
|
|
|
|
|
System.err.println("Mark to pick is null.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (port){
|
|
|
|
|
Assets3D.ccwNextArrow.setX(gpsConverter.convertGPS(chosenMark.getPosition()).getX());
|
|
|
|
|
Assets3D.ccwNextArrow.setZ(gpsConverter.convertGPS(chosenMark.getPosition()).getY());
|
|
|
|
|
Assets3D.ccwNextArrow.getMesh().setVisible(true);
|
|
|
|
|
Assets3D.cwNextArrow.getMesh().setVisible(false);
|
|
|
|
|
} else {
|
|
|
|
|
Assets3D.cwNextArrow.setX(gpsConverter.convertGPS(chosenMark.getPosition()).getX());
|
|
|
|
|
Assets3D.cwNextArrow.setZ(gpsConverter.convertGPS(chosenMark.getPosition()).getY());
|
|
|
|
|
Assets3D.cwNextArrow.getMesh().setVisible(true);
|
|
|
|
|
Assets3D.ccwNextArrow.getMesh().setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Swap the colour of the next mark to pass with the last mark passed
|
|
|
|
|
* @param leg boat has started on
|
|
|
|
|
*/
|
|
|
|
|
private void swapColours(Leg leg) {
|
|
|
|
|
private void changeNextMark(Leg leg){
|
|
|
|
|
CompoundMark start = leg.getStartCompoundMark();
|
|
|
|
|
CompoundMark end = leg.getEndCompoundMark();
|
|
|
|
|
|
|
|
|
|
@ -484,23 +819,28 @@ public class RaceViewController extends Controller {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Shape3D start1 = view3D.getShape(start.getMark1().getSourceID()).getMesh();
|
|
|
|
|
Shape3D end1 = view3D.getShape(end.getMark1().getSourceID()).getMesh();
|
|
|
|
|
|
|
|
|
|
Material nextMark = start1.getMaterial();
|
|
|
|
|
Material lastMark = end1.getMaterial();
|
|
|
|
|
|
|
|
|
|
start1.setMaterial(lastMark);
|
|
|
|
|
if(start.getMark2() != null) {
|
|
|
|
|
Shape3D start2 = view3D.getShape(start.getMark2().getSourceID()).getMesh();
|
|
|
|
|
start2.setMaterial(lastMark);
|
|
|
|
|
//find direction coming
|
|
|
|
|
double angle = gpsConverter.getAngle(gpsConverter.convertGPS(start.getMark1().getPosition()),
|
|
|
|
|
gpsConverter.convertGPS(end.getMark1Position()));
|
|
|
|
|
angle = (Math.toDegrees(angle) % 360 + 360) % 360;
|
|
|
|
|
int dir = 0; //0 = top 1 = right 2 = down 3 = down
|
|
|
|
|
if (angle < 315){
|
|
|
|
|
if (angle > 45){
|
|
|
|
|
dir ++;
|
|
|
|
|
}
|
|
|
|
|
if (angle > 135){
|
|
|
|
|
dir ++;
|
|
|
|
|
}
|
|
|
|
|
if (angle > 225){
|
|
|
|
|
dir ++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end1.setMaterial(nextMark);
|
|
|
|
|
if(end.getMark2() != null) {
|
|
|
|
|
Shape3D end2 = view3D.getShape(end.getMark2().getSourceID()).getMesh();
|
|
|
|
|
end2.setMaterial(nextMark);
|
|
|
|
|
}
|
|
|
|
|
Mark startMark1 = end.getMark1();
|
|
|
|
|
Mark startMark2 = end.getMark2();
|
|
|
|
|
|
|
|
|
|
setNextMarkArrowDirection(startMark1, startMark2, end.getRoundingType(), dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -544,13 +884,17 @@ public class RaceViewController extends Controller {
|
|
|
|
|
// set table data
|
|
|
|
|
boatInfoTable.setItems(sortedBoats);
|
|
|
|
|
boatTeamColumn.setCellValueFactory(
|
|
|
|
|
cellData -> cellData.getValue().nameProperty());
|
|
|
|
|
cellData -> cellData.getValue().nameProperty()
|
|
|
|
|
);
|
|
|
|
|
boatSpeedColumn.setCellValueFactory(
|
|
|
|
|
cellData -> cellData.getValue().currentSpeedProperty());
|
|
|
|
|
cellData -> cellData.getValue().currentSpeedProperty()
|
|
|
|
|
);
|
|
|
|
|
boatMarkColumn.setCellValueFactory(
|
|
|
|
|
cellData -> cellData.getValue().legProperty());
|
|
|
|
|
boatPlacingColumn.setCellValueFactory(
|
|
|
|
|
cellData -> cellData.getValue().placingProperty());
|
|
|
|
|
cellData -> cellData.getValue().legProperty()
|
|
|
|
|
);
|
|
|
|
|
boatHealthColumn.setCellValueFactory(
|
|
|
|
|
cellData -> cellData.getValue().healthProperty()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//Kind of ugly, but allows for formatting an observed speed.
|
|
|
|
|
boatSpeedColumn.setCellFactory(
|
|
|
|
|
@ -650,6 +994,101 @@ public class RaceViewController extends Controller {
|
|
|
|
|
}.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Animation timer loop for the speedometer
|
|
|
|
|
*/
|
|
|
|
|
private void speedometerLoop(){
|
|
|
|
|
new AnimationTimer(){
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long arg0){
|
|
|
|
|
if (raceState.getRaceStatusEnum() == RaceStatusEnum.FINISHED) {
|
|
|
|
|
stop(); // stop the timer
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
//Set the current speed value of the boat
|
|
|
|
|
gauge.setValue(raceState.getBoat(raceState.getPlayerBoatID()).getCurrentSpeed());
|
|
|
|
|
fGauge.getGauge().setValue(raceState.getBoat(raceState.getPlayerBoatID()).getCurrentSpeed());
|
|
|
|
|
|
|
|
|
|
//Create list with sorted boat placements
|
|
|
|
|
List<VisualiserBoat> boatList = boatInfoTable.getItems();
|
|
|
|
|
for (VisualiserBoat boat : boatList){
|
|
|
|
|
if(raceState.getPlayerBoatID()==boat.getSourceID()){
|
|
|
|
|
//Set boat current placement value as title of speedometer
|
|
|
|
|
gauge.titleProperty().setValue("Position: " + (boatInfoTable.getItems().indexOf(boat)+1));
|
|
|
|
|
fGauge.getGauge().titleProperty().setValue("Position: " + (boatInfoTable.getItems().indexOf(boat)+1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (BoatNotFoundException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Animation timer for health
|
|
|
|
|
*/
|
|
|
|
|
private void healthLoop(){
|
|
|
|
|
new AnimationTimer(){
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long arg0){
|
|
|
|
|
if (raceState.getRaceStatusEnum() == RaceStatusEnum.FINISHED) {
|
|
|
|
|
stop(); // stop the timer
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
//Check if boat is dead
|
|
|
|
|
if(raceState.getBoat(raceState.getPlayerBoatID()).getHealth()<=0){
|
|
|
|
|
if(!deathPane.isDisable()) {
|
|
|
|
|
deathPane.setVisible(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (BoatNotFoundException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(VisualiserBoat boat : raceState.getBoats()){
|
|
|
|
|
for (HealthEffect fp : healthEffectList){
|
|
|
|
|
|
|
|
|
|
if(fp.getSourceID()==boat.getSourceID()){
|
|
|
|
|
if(boat.getHealth()<=0){
|
|
|
|
|
//Boat is dead. Don't check it anymore for hp
|
|
|
|
|
fp.displayDeath(fp.getSourceID()==raceState.getPlayerBoatID());
|
|
|
|
|
Annotation3D sharks = new Annotation3D(Assets3D.getSharks());
|
|
|
|
|
sharks.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
|
|
|
|
|
sharks.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
|
|
|
|
|
viewSubjects.add(sharks);
|
|
|
|
|
new AnimationTimer(){
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
sharks.setHeading(sharks.getHeading().getAngle() - 0.5);
|
|
|
|
|
}
|
|
|
|
|
}.start();
|
|
|
|
|
|
|
|
|
|
fp.setSourceID(0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
//Speed up tick when <=10 hp
|
|
|
|
|
if(boat.getHealth()<=10){
|
|
|
|
|
fp.flash(System.currentTimeMillis(), 300, boat.getSourceID()==raceState.getPlayerBoatID());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
//Visual indication of low hp
|
|
|
|
|
if(boat.getHealth()<=20) {
|
|
|
|
|
//fp.setVisible(true);
|
|
|
|
|
fp.flash(System.currentTimeMillis(), 500, boat.getSourceID()==raceState.getPlayerBoatID());
|
|
|
|
|
} else {
|
|
|
|
|
fp.setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* toggles if the info table is shown
|
|
|
|
|
*/
|
|
|
|
|
@ -783,25 +1222,115 @@ public class RaceViewController extends Controller {
|
|
|
|
|
|
|
|
|
|
private void bigMap(){
|
|
|
|
|
if (mapToggle){
|
|
|
|
|
raceCanvas.widthProperty().bind(canvasBase.widthProperty());
|
|
|
|
|
raceCanvas.heightProperty().bind(canvasBase.heightProperty());
|
|
|
|
|
raceCanvas.widthProperty().bind(canvasBase2.widthProperty());
|
|
|
|
|
raceCanvas.heightProperty().bind(canvasBase2.heightProperty());
|
|
|
|
|
|
|
|
|
|
raceCanvas.setFullScreen(true);
|
|
|
|
|
raceCanvas.setOpacity(0.6);
|
|
|
|
|
|
|
|
|
|
canvasBase1.getChildren().remove(raceCanvas);
|
|
|
|
|
canvasBase.getChildren().add(1, raceCanvas);
|
|
|
|
|
canvasBase2.getChildren().add(0, raceCanvas);
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
raceCanvas.widthProperty().bind(canvasBase1.widthProperty());
|
|
|
|
|
raceCanvas.heightProperty().bind(canvasBase1.heightProperty());
|
|
|
|
|
|
|
|
|
|
raceCanvas.setFullScreen(false);
|
|
|
|
|
raceCanvas.setOpacity(1);
|
|
|
|
|
|
|
|
|
|
canvasBase.getChildren().remove(raceCanvas);
|
|
|
|
|
canvasBase2.getChildren().remove(raceCanvas);
|
|
|
|
|
canvasBase1.getChildren().add(0, raceCanvas);
|
|
|
|
|
}
|
|
|
|
|
mapToggle = !mapToggle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* FXML method for death button
|
|
|
|
|
*/
|
|
|
|
|
public void deathOKPressed(){
|
|
|
|
|
deathPane.setDisable(true);
|
|
|
|
|
deathPane.setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private AnimationTimer seagullGoTo = new AnimationTimer() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
for (Subject3D seagull: seagulls) {
|
|
|
|
|
if (seagullsGoToX.get(seagull).size() > 0) {
|
|
|
|
|
//System.out.println(xPosition + " " + yPosition);
|
|
|
|
|
seagull.setHeading(GPSConverter.getAngle(new GraphCoordinate(seagull.getX(), seagull.getZ()),
|
|
|
|
|
new GraphCoordinate(seagullsGoToX.get(seagull).get(0), seagullsGoToY.get(seagull).get(0))));
|
|
|
|
|
double delx = seagullsGoToX.get(seagull).get(0) - seagull.getX();
|
|
|
|
|
double dely = seagullsGoToY.get(seagull).get(0) - seagull.getZ();
|
|
|
|
|
double scale = seagullSpeed / Math.sqrt(delx * delx + dely * dely);
|
|
|
|
|
if (scale < 1) {
|
|
|
|
|
seagull.setX(seagull.getX() + delx * scale);
|
|
|
|
|
seagull.setZ(seagull.getZ() + dely * scale);
|
|
|
|
|
} else {
|
|
|
|
|
seagullsGoToX.get(seagull).remove(0);
|
|
|
|
|
seagullsGoToY.get(seagull).remove(0);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
randSeagullNewAction(seagull);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void randSeagullNewAction(Subject3D seagull){
|
|
|
|
|
Random rand = new Random();
|
|
|
|
|
int nextAction = rand.nextInt(1);
|
|
|
|
|
switch(nextAction){
|
|
|
|
|
case 0:
|
|
|
|
|
//do a straight line
|
|
|
|
|
double nextX = rand.nextInt((int)gpsConverter.getLongitudeFactor());
|
|
|
|
|
/*if (nextX > this.xBound/2){
|
|
|
|
|
nextX = - nextX/2;
|
|
|
|
|
}*/
|
|
|
|
|
double nextY = rand.nextInt((int)gpsConverter.getLatitudeFactor());
|
|
|
|
|
/*if (nextY > this.yBound/2){
|
|
|
|
|
nextY = - nextY/2;
|
|
|
|
|
}*/
|
|
|
|
|
seagullsGoToX.get(seagull).add(nextX);
|
|
|
|
|
seagullsGoToY.get(seagull).add(nextY);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
//do a octogan circle
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() - 3);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() - 3);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX());
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() + 3);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() + 6);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() + 6);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX() + 3);
|
|
|
|
|
seagullsGoToX.get(seagull).add(seagull.getX());
|
|
|
|
|
//y
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 3);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 6);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 9);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 9);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 6);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ() - 3);
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ());
|
|
|
|
|
seagullsGoToY.get(seagull).add(seagull.getZ());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addToFlock(){
|
|
|
|
|
Annotation3D newSeagull = new Annotation3D(new SeagullMesh());
|
|
|
|
|
newSeagull.setY(-15);
|
|
|
|
|
newSeagull.setX(67);
|
|
|
|
|
newSeagull.setZ(43);
|
|
|
|
|
newSeagull.setXRot(0);
|
|
|
|
|
seagulls.add(newSeagull);
|
|
|
|
|
seagullsGoToX.put(newSeagull, new ArrayList<>());
|
|
|
|
|
seagullsGoToY.put(newSeagull, new ArrayList<>());
|
|
|
|
|
viewSubjects.add(newSeagull);
|
|
|
|
|
randSeagullNewAction(newSeagull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|