You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
11 KiB

package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.shape.MeshView;
import mock.app.Event;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RequestToJoinEnum;
import visualiser.gameController.ControllerClient;
import visualiser.layout.SeaSurface;
import visualiser.layout.Subject3D;
import visualiser.layout.View3D;
import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRaceEvent;
import visualiser.model.VisualiserRaceState;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller for Hosting a game.
*/
public class InGameLobbyController extends Controller {
@FXML
private ImageView imageView;
@FXML
AnchorPane gameLobbyWrapper;
@FXML
GridPane playerContainer;
@FXML
private Label playerLabel;
@FXML
private Label playerLabel2;
@FXML
private Label playerLabel3;
@FXML
private Label playerLabel4;
@FXML
private Label playerLabel5;
@FXML
private Label playerLabel6;
@FXML
private Label countdownLabel;
@FXML
private AnchorPane countdownTenPane;
@FXML
private Label countdownTenText;
private Event game;
private View3D playerBoat;
private VisualiserRaceEvent visualiserRaceEvent;
private boolean isHost;
private ControllerClient controllerClient;
private ArrayList<Label> allPlayerLabels;
private ObservableList<Subject3D> subjects = FXCollections.observableArrayList();
private StlMeshImporter importer;
private PopulatePlayers lobbyUpdateListener;
@Override
public void initialize(URL location, ResourceBundle resources) {
allPlayerLabels = new ArrayList(Arrays.asList(playerLabel, playerLabel2,
playerLabel3,
playerLabel4,
playerLabel5,
playerLabel6));
URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
importer = new StlMeshImporter();
importer.read(asset);
lobbyUpdateListener = new PopulatePlayers();
}
private void resetLobby(){
for (Label label: allPlayerLabels){
label.setText("No Player");
}
List<Node> nodeCopy = new ArrayList(playerContainer.getChildren());
for (Node node: nodeCopy){
if (node instanceof View3D){
playerContainer.getChildren().remove(node);
}
}
}
public class PopulatePlayers implements ListChangeListener {
@Override
public void onChanged(Change change) {
Platform.runLater(
() -> {
while (change.next()){
if (change.wasAdded() || change.wasRemoved() || change.wasUpdated()){
try{
resetLobby();
int count = 0;
int row = 0;
for (VisualiserBoat boat :visualiserRaceEvent.getVisualiserRaceState().getBoats()) {
View3D playerBoatToSet = new View3D();
playerBoatToSet.setItems(subjects);
playerContainer.add(playerBoatToSet, (count % 3) , row);
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
SeaSurface sea = new SeaSurface(750, 200, 250, 0, 210);
subjects.add(sea.getSurface());
MeshView mesh = new MeshView(importer.getImport());
Subject3D subject = new Subject3D(mesh);
subjects.add(subject);
playerBoatToSet.setDistance(50);
playerBoatToSet.setYaw(45);
playerBoatToSet.setPitch(20);
AnimationTimer rotate = new AnimationTimer() {
@Override
public void handle(long now) {
subject.setHeading(subject.getHeading().getAngle() + 0.1);
}
};
rotate.start();
allPlayerLabels.get(count).setText("Player: " + boat.getSourceID());
allPlayerLabels.get(count).toFront();
count += 1;
if (count > 2){
row = 1;
}
}
}
catch(ConcurrentModificationException e){
}
}
}
}
);
}
}
/*
private void populatePlayers(ListChangeListener.Change change){
}*/
/**
* Starts the race.
*/
private void startRace() {
//Initialises the race clock.
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
//Starts the race countdown timer.
countdownTimer();
}
public AnchorPane gameLobbyWrapper(){
return gameLobbyWrapper;
}
/**
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClock(VisualiserRaceState visualiserRace) {
//Remaining time.
initialiseRaceClockDuration(visualiserRace);
}
/**
* Initialises the race duration label.
* @param visualiserRace The race to get data from.
*/
private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) {
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
countdownLabel.setText(newValue);
});
});
}
/**
* Countdown timer until race starts.
*/
private void countdownTimer() {
new AnimationTimer() {
@Override
public void handle(long arg0) {
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
try {
long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime());
if(interval<=10000){
countdownLabel.setVisible(false);
countdownTenPane.setVisible(true);
countdownTenText.setVisible(true);
}
countdownText(interval);
} catch (Exception e){
}
//If the race has reached the preparatory phase, or has started...
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
//Stop this timer.
stop();
//Hide this, and display the race controller.
gameLobbyWrapper.setVisible(false);
visualiserRaceEvent.getVisualiserRaceState().getBoats().removeListener(lobbyUpdateListener);
countdownTenPane.setVisible(false);
countdownLabel.setVisible(true);
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
}
}
}.start();
}
/**
* Hosts a game.
*/
public void enterGameLobby(Socket socket, boolean isHost){
try {
this.visualiserRaceEvent = new VisualiserRaceEvent(socket, RequestToJoinEnum.PARTICIPANT);
this.isHost = isHost;
this.controllerClient = visualiserRaceEvent.getControllerClient();
this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener);
gameLobbyWrapper.setVisible(true);
startRace();
} catch (IOException e) {
//TODO should probably let this propagate, so that we only enter this scene if everything works
Logger.getGlobal().log(Level.WARNING, "Could not connect to server.", e);
}
}
/**
* Menu button pressed. Prompt alert then return to menu
*/
public void menuBtnPressed(){
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Quitting race");
alert.setContentText("Do you wish to quit the race?");
alert.setHeaderText("You are about to quit the race");
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == ButtonType.OK){
visualiserRaceEvent.terminate();
gameLobbyWrapper.setVisible(false);
parent.enterTitle();
}
}
/**
* Start button pressed. Currently only prints out start
*/
public void startBtnPressed(){
System.out.println("Should start the race. This button is only visible for the host");
}
public void joinSpecPressed(){
System.out.println("Spectator list pressed. Joining spectators");
}
public void joinRacePressed(){
System.out.println("Empty race user pane pressed. Joining racers");
}
private void countdownText(long interval){
if (interval <=4000){
return;
}
if (interval <=5000){
//countdownTenText.setText("5");
return;
}
if (interval <=6000){
countdownTenText.setText("1");
return;
}
if (interval <=7000){
countdownTenText.setText("2");
return;
}
if (interval <=8000){
countdownTenText.setText("3");
return;
}
if (interval <=9000){
countdownTenText.setText("4");
return;
}
if (interval <=10000){
countdownTenText.setText("5");
return;
}
}
}