mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Merge branch 'develop' into Story80_BoatCustomization
This commit is contained in:
@@ -27,6 +27,7 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
|
||||
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();
|
||||
|
||||
public MainServerThread() {
|
||||
new GameState("localhost");
|
||||
try {
|
||||
serverSocket = new ServerSocket(PORT);
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -25,9 +25,10 @@ import seng302.utilities.GeoUtility;
|
||||
*/
|
||||
public class Yacht {
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface YachtLocationListener {
|
||||
void notifyLocation(Yacht yacht, double lat, double lon, double heading, double velocity);
|
||||
void notifyLocation(Yacht yacht, double lat, double lon, double heading, double velocity, boolean sailIn);
|
||||
}
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(Yacht.class);
|
||||
@@ -54,7 +55,7 @@ public class Yacht {
|
||||
//SERVER SIDE
|
||||
public static final Double TURN_STEP = 5.0; //This should be in some utils class somewhere 2bh. Public for tests sake.
|
||||
private Double lastHeading;
|
||||
private Boolean sailIn;
|
||||
private Boolean sailIn = false;
|
||||
private GeoPoint location;
|
||||
private Integer boatStatus;
|
||||
private Double velocity;
|
||||
@@ -76,6 +77,7 @@ public class Yacht {
|
||||
private CompoundMark lastMarkRounded;
|
||||
private Integer positionInt = 0;
|
||||
private Color colour;
|
||||
private Boolean clientSailsIn = true;
|
||||
|
||||
public Yacht(String boatType, Integer sourceId, String hullID, String shortName,
|
||||
String boatName, String country) {
|
||||
@@ -631,6 +633,9 @@ public class Yacht {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
public void toggleClientSail() {
|
||||
clientSailsIn = !clientSailsIn;
|
||||
}
|
||||
|
||||
public Double getVelocity() {
|
||||
return velocity;
|
||||
@@ -644,13 +649,17 @@ public class Yacht {
|
||||
return distanceToCurrentMark;
|
||||
}
|
||||
|
||||
public Boolean getClientSailsIn(){
|
||||
return clientSailsIn;
|
||||
}
|
||||
|
||||
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
||||
setLocation(lat, lng);
|
||||
this.heading = heading;
|
||||
this.velocity = velocity;
|
||||
updateVelocityProperty(velocity);
|
||||
for (YachtLocationListener yll : locationListeners) {
|
||||
yll.notifyLocation(this, lat, lng, heading, velocity);
|
||||
yll.notifyLocation(this, lat, lng, heading, velocity, clientSailsIn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ import seng302.model.stream.packets.StreamPacket;
|
||||
*/
|
||||
public class ClientToServerThread implements Runnable {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Functional interface for receiving packets from client socket.
|
||||
*/
|
||||
|
||||
@@ -257,6 +257,8 @@ public class GameClient {
|
||||
private void processRaceStatusUpdate(RaceStatusData data) {
|
||||
if (allXMLReceived()) {
|
||||
raceState.updateState(data);
|
||||
if (raceView != null)
|
||||
raceView.getGameView().setWindDir(raceState.getWindDirection());
|
||||
for (long[] boatData : data.getBoatData()) {
|
||||
Yacht yacht = allBoatsMap.get((int) boatData[0]);
|
||||
yacht.setEstimateTimeTillNextMark(raceState.getRaceTime() - boatData[1]);
|
||||
@@ -310,7 +312,9 @@ public class GameClient {
|
||||
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
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_IN); break;
|
||||
socketThread.sendBoatAction(BoatAction.SAILS_IN);
|
||||
raceView.getGameView().getPlayerYacht().toggleClientSail();
|
||||
break;
|
||||
case PAGE_UP:
|
||||
case PAGE_DOWN:
|
||||
socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break;
|
||||
|
||||
@@ -9,10 +9,14 @@ import java.util.Map;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
@@ -53,6 +57,8 @@ public class GameView extends Pane {
|
||||
private double referencePointX, referencePointY;
|
||||
private double metersPerPixelX, metersPerPixelY;
|
||||
|
||||
final double SCALE_DELTA = 1.1;
|
||||
|
||||
private Text fpsDisplay = new Text();
|
||||
private Polygon raceBorder = new CourseBoundary();
|
||||
|
||||
@@ -80,6 +86,26 @@ public class GameView extends Pane {
|
||||
private Double frameRate = 60.0;
|
||||
private int frameTimeIndex = 0;
|
||||
private boolean arrayFilled = false;
|
||||
private Yacht playerYacht;
|
||||
private double windDir = 0.0;
|
||||
|
||||
double scaleFactor = 1;
|
||||
|
||||
public void zoomOut() {
|
||||
scaleFactor = 0.95;
|
||||
for (Node child : getChildren()) {
|
||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
public void zoomIn() {
|
||||
scaleFactor = 1.05;
|
||||
for (Node child : getChildren()) {
|
||||
child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
private enum ScaleDirection {
|
||||
HORIZONTAL,
|
||||
@@ -96,6 +122,45 @@ public class GameView extends Pane {
|
||||
gameObjects.add(fpsDisplay);
|
||||
gameObjects.add(raceBorder);
|
||||
gameObjects.add(markers);
|
||||
//
|
||||
// this.setOnKeyPressed(new EventHandler<KeyEvent>() {
|
||||
// @Override public void handle(KeyEvent event) {
|
||||
// event.consume();
|
||||
// switch (event.getCode()) {
|
||||
// case Z:
|
||||
// scaleFactor = scaleFactor * 1.2;
|
||||
// break;
|
||||
// case X:
|
||||
// scaleFactor = scaleFactor * 0.8;
|
||||
// break;
|
||||
// }
|
||||
// if (event.getCode() == KeyCode.Z || event.getCode() == KeyCode.X) {
|
||||
// for (Node child : getChildren()) {
|
||||
// child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
// child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// this.setOnScroll(new EventHandler<ScrollEvent>() {
|
||||
// @Override public void handle(ScrollEvent event) {
|
||||
// event.consume();
|
||||
// if (event.getDeltaY() == 0) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// double scaleFactor =
|
||||
// (event.getDeltaY() > 0)
|
||||
// ? SCALE_DELTA
|
||||
// : 1/SCALE_DELTA;
|
||||
// for (Node child : getChildren()) {
|
||||
// child.setScaleX(child.getScaleX() * scaleFactor);
|
||||
// child.setScaleY(child.getScaleY() * scaleFactor);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
initializeTimer();
|
||||
}
|
||||
|
||||
@@ -324,10 +389,10 @@ public class GameView extends Pane {
|
||||
boatObjectGroup.getChildren().add(newBoat);
|
||||
trails.getChildren().add(newBoat.getTrail());
|
||||
// TODO: 1/08/17 Make this less vile to look at.
|
||||
yacht.addLocationListener((boat, lat, lon, heading, velocity) ->{
|
||||
yacht.addLocationListener((boat, lat, lon, heading, velocity, sailIn) ->{
|
||||
BoatObject bo = boatObjects.get(boat);
|
||||
Point2D p2d = findScaledXY(lat, lon);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity);
|
||||
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
|
||||
// annotations.get(boat).setLayoutX(p2d.getX());
|
||||
// annotations.get(boat).setLayoutY(p2d.getY());
|
||||
// annotations.get(boat).setLocation(100d, 100d);
|
||||
@@ -345,7 +410,9 @@ public class GameView extends Pane {
|
||||
gameObjects.addAll(wakes);
|
||||
gameObjects.addAll(annotationsGroup);
|
||||
gameObjects.addAll(boatObjectGroup);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void createAndBindAnnotationBox (Yacht yacht, Paint colour) {
|
||||
@@ -562,11 +629,23 @@ public class GameView extends Pane {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
|
||||
public void setWindDir(double windDir) {
|
||||
this.windDir = windDir;
|
||||
}
|
||||
|
||||
|
||||
public void startRace () {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
public Yacht getPlayerYacht() {
|
||||
return playerYacht;
|
||||
}
|
||||
|
||||
public void setBoatAsPlayer (Yacht playerYacht) {
|
||||
this.playerYacht = playerYacht;
|
||||
this.playerYacht.toggleClientSail();
|
||||
boatObjects.get(playerYacht).setAsPlayer();
|
||||
annotations.get(playerYacht).addAnnotation(
|
||||
"velocity",
|
||||
@@ -579,5 +658,6 @@ public class GameView extends Pane {
|
||||
annotationsGroup.getChildren().remove(annotations.get(playerYacht));
|
||||
gameObjects.add(annotations.get(playerYacht));
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,4 +595,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
this.courseData = raceData;
|
||||
gameView.updateBorder(raceData.getCourseLimit());
|
||||
}
|
||||
|
||||
public GameView getGameView() {
|
||||
return gameView;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class StartScreenController implements Initializable {
|
||||
*/
|
||||
@FXML
|
||||
public void hostButtonPressed() {
|
||||
new GameState(getLocalHostIp());
|
||||
// new GameState(getLocalHostIp());
|
||||
gameClient = new GameClient(holder);
|
||||
gameClient.runAsHost(getLocalHostIp(), 4942);
|
||||
// try {
|
||||
|
||||
@@ -30,9 +30,11 @@ public class BoatObject extends Group {
|
||||
private double xVelocity;
|
||||
private double yVelocity;
|
||||
private double lastHeading;
|
||||
private double sailState;
|
||||
//Graphical objects
|
||||
private Polyline trail = new Polyline();
|
||||
private Polygon boatPoly;
|
||||
private Polygon sail;
|
||||
private Wake wake;
|
||||
private Line leftLayLine;
|
||||
private Line rightLayline;
|
||||
@@ -94,7 +96,16 @@ public class BoatObject extends Group {
|
||||
trail.setCache(true);
|
||||
wake = new Wake(0, -BOAT_HEIGHT);
|
||||
wake.setVisible(true);
|
||||
super.getChildren().addAll(boatPoly);//, annotationBox);
|
||||
|
||||
sail = new Polygon(0.0,BOAT_HEIGHT / 4,
|
||||
0.0, BOAT_HEIGHT);
|
||||
sailState = 0;
|
||||
sail.setStrokeWidth(2.0);
|
||||
sail.setStroke(Color.BLACK);
|
||||
sail.setFill(Color.TRANSPARENT);
|
||||
sail.setCache(true);
|
||||
super.getChildren().clear();
|
||||
super.getChildren().addAll(boatPoly, sail);
|
||||
}
|
||||
|
||||
public void setFill (Paint value) {
|
||||
@@ -105,19 +116,30 @@ public class BoatObject extends Group {
|
||||
|
||||
/**
|
||||
* Moves the boat and its children annotations to coordinates specified
|
||||
*
|
||||
* @param x The X coordinate to move the boat to
|
||||
* @param x The X coordinate to move the boat to
|
||||
* @param y The Y coordinate to move the boat to
|
||||
* @param rotation The rotation by which the boat moves
|
||||
* @param velocity The velocity the boat is moving
|
||||
* @param sailIn
|
||||
*/
|
||||
public void moveTo(double x, double y, double rotation, double velocity) {
|
||||
public void moveTo(double x, double y, double rotation, double velocity, Boolean sailIn, double windDir) {
|
||||
Double dx = Math.abs(boatPoly.getLayoutX() - x);
|
||||
Double dy = Math.abs(boatPoly.getLayoutY() - y);
|
||||
Platform.runLater(() -> {
|
||||
rotateTo(rotation);
|
||||
rotateTo(rotation, sailIn, windDir);
|
||||
boatPoly.setLayoutX(x);
|
||||
boatPoly.setLayoutY(y);
|
||||
if (sailIn) {
|
||||
// sail.getPoints().clear();
|
||||
// sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0);
|
||||
// sail.getPoints().addAll(0.0, 0.0, 24.0, 0.0);
|
||||
sail.setLayoutX(x);
|
||||
sail.setLayoutY(y);
|
||||
} else {
|
||||
animateSail();
|
||||
sail.setLayoutX(x);
|
||||
sail.setLayoutY(y);
|
||||
}
|
||||
wake.setLayoutX(x);
|
||||
wake.setLayoutY(y);
|
||||
});
|
||||
@@ -142,8 +164,65 @@ public class BoatObject extends Group {
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateTo(double rotation) {
|
||||
boatPoly.getTransforms().setAll(new Rotate(rotation));
|
||||
private Double normalizeHeading(double heading, double windDirection) {
|
||||
Double normalizedHeading = heading - windDirection;
|
||||
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
|
||||
return normalizedHeading;
|
||||
}
|
||||
|
||||
|
||||
private void rotateTo(double heading, boolean sailsIn, double windDir) {
|
||||
boatPoly.getTransforms().setAll(new Rotate(heading));
|
||||
if (sailsIn) {
|
||||
Double sailWindOffset = 30.0;
|
||||
Double upwindAngleLimit = 15.0;
|
||||
Double downwindAngleLimit = 10.0; //Upwind from normalised horizontal
|
||||
Double normalizedHeading = normalizeHeading(heading, windDir);
|
||||
if (normalizedHeading < 180) {
|
||||
sail.getTransforms().setAll(new Rotate(windDir + 90 + sailWindOffset));
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(0.0, 0.0, 4.0, -1.5, 8.0, -3.0, 12.0, -3.5, 16.0, -3.0, 20.0, -1.5, 24.0, 0.0);
|
||||
if (normalizedHeading > 90 + sailWindOffset){
|
||||
sail.getTransforms().setAll(new Rotate(heading + downwindAngleLimit));
|
||||
}
|
||||
if (normalizedHeading < sailWindOffset + upwindAngleLimit){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 90 - upwindAngleLimit));
|
||||
}
|
||||
} else {
|
||||
sail.getTransforms().setAll(new Rotate(windDir + 90 - sailWindOffset));
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(0.0, 0.0, 4.0, 1.5, 8.0, 3.0, 12.0, 3.5, 16.0, 3.0, 20.0, 1.5, 24.0, 0.0);
|
||||
if (normalizedHeading < 270 - sailWindOffset){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 180 - downwindAngleLimit));
|
||||
}
|
||||
if (normalizedHeading > 360 - (sailWindOffset + upwindAngleLimit)){
|
||||
sail.getTransforms().setAll(new Rotate(heading + 90 + upwindAngleLimit));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sail.getTransforms().setAll(new Rotate(windDir));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void animateSail(){
|
||||
Double[] points = new Double[200];
|
||||
double amplitude = 2.0;
|
||||
double period = 10;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
points[i * 2] = amplitude * Math.sin(((Math.PI * i) / period + sailState));
|
||||
points[i * 2 + 1] = (BOAT_HEIGHT * i) / BOAT_HEIGHT / 2;
|
||||
points[199 - (i * 2)] = (BOAT_HEIGHT * i) / BOAT_HEIGHT / 2;
|
||||
points[199 - (i * 2 + 1)] = amplitude * Math.sin(((Math.PI * i) / period + sailState));
|
||||
}
|
||||
if (sailState == - 2 * Math.PI) {
|
||||
sailState = 0;
|
||||
} else {
|
||||
sailState = sailState - Math.PI / 5;
|
||||
}
|
||||
sail.getPoints().clear();
|
||||
sail.getPoints().addAll(points);
|
||||
|
||||
}
|
||||
|
||||
public void updateLocation() {
|
||||
@@ -275,11 +354,12 @@ public class BoatObject extends Group {
|
||||
boatPoly.setStroke(Color.BLACK);
|
||||
boatPoly.setStrokeWidth(3);
|
||||
isPlayer = true;
|
||||
animateSail();
|
||||
}
|
||||
|
||||
public void setTrajectory(double heading, double velocity) {
|
||||
public void setTrajectory(double heading, double velocity, double windDir) {
|
||||
wake.setRotation(lastHeading - heading, velocity);
|
||||
rotateTo(heading);
|
||||
rotateTo(heading, false, windDir);
|
||||
xVelocity = Math.cos(Math.toRadians(heading)) * velocity;
|
||||
yVelocity = Math.sin(Math.toRadians(heading)) * velocity;
|
||||
lastHeading = heading;
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
<race-name>AC35</race-name>
|
||||
<race-size>6</race-size>
|
||||
<time-scale>10.0</time-scale>
|
||||
<wind-direction>135</wind-direction>
|
||||
<windDir-direction>135</windDir-direction>
|
||||
</configurations>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import cucumber.api.CucumberOptions;
|
||||
import cucumber.api.junit.Cucumber;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Created by kre39 on 7/08/17.
|
||||
*/
|
||||
|
||||
@RunWith(Cucumber.class)
|
||||
@CucumberOptions(features = "src/test/java/features")
|
||||
public class RunCucumberTests {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Feature: SailsToggle
|
||||
Scenario: User toggles in sail
|
||||
Given The game is running
|
||||
When the user has pressed "shift"
|
||||
Then the sails are "in"
|
||||
@@ -0,0 +1,31 @@
|
||||
package seng302.visualiser.map;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
|
||||
/**
|
||||
* Created by kre39 on 6/08/17.
|
||||
*/
|
||||
public class BoatSailAnimationToggleTest {
|
||||
|
||||
private Yacht yacht;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception{
|
||||
yacht = new Yacht("Yacht", 1, "YACHT", "YAC", "Test Yacht", "NZ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sailToggleTest() throws Exception {
|
||||
assertFalse(yacht.getSailIn());
|
||||
yacht.toggleClientSail();
|
||||
assertFalse(yacht.getSailIn());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package steps;
|
||||
|
||||
import cucumber.api.java.en.Given;
|
||||
import cucumber.api.java.en.Then;
|
||||
import cucumber.api.java.en.When;
|
||||
import java.util.ArrayList;
|
||||
import org.junit.Assert;
|
||||
import seng302.gameServer.GameStages;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.MainServerThread;
|
||||
import seng302.gameServer.server.messages.BoatAction;
|
||||
import seng302.model.Yacht;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by kre39 on 7/08/17.
|
||||
*/
|
||||
public class ToggleSailSteps {
|
||||
|
||||
|
||||
MainServerThread mst;
|
||||
ClientToServerThread client;
|
||||
boolean sailsIn = false;
|
||||
long startTime;
|
||||
private Yacht yacht;
|
||||
|
||||
|
||||
|
||||
@Given("^The game is running$")
|
||||
public void the_game_is_running() throws Throwable {
|
||||
mst = new MainServerThread();
|
||||
client = new ClientToServerThread("localhost", 4942);
|
||||
GameState.setCurrentStage(GameStages.RACING);
|
||||
Thread.sleep(200); // Sleep needed to help the threads all be up to speed with each other
|
||||
Yacht yacht = (new ArrayList<>(GameState.getYachts().values())).get(0);
|
||||
Assert.assertFalse(yacht.getSailIn());
|
||||
}
|
||||
|
||||
|
||||
@When("^the user has pressed \"([^\"]*)\"$")
|
||||
public void the_user_has_pressed(String arg1) throws Throwable {
|
||||
startTime = System.currentTimeMillis();
|
||||
if (arg1 == "shift") {
|
||||
client.sendBoatAction(BoatAction.SAILS_IN);
|
||||
}
|
||||
}
|
||||
|
||||
@Then("^the sails are \"([^\"]*)\"$")
|
||||
public void the_sails_are(String arg1) throws Throwable {
|
||||
Thread.sleep(200); // Sleep needed to help the threads all be up to speed with each other
|
||||
Yacht yacht = (new ArrayList<>(GameState.getYachts().values())).get(0);
|
||||
if (arg1 == "in") {
|
||||
Assert.assertTrue(yacht.getSailIn());
|
||||
} else {
|
||||
Assert.assertFalse(yacht.getSailIn());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user