mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch 'Story1117_Course_Boundary_Collision' into 'develop'
Checked if a boat has crossed the boundary/course limit, if so, bounce the boat back. # Boundary crossing detection * if a boat has crossed the boundary, it will be bounced back to where it has came from # Testing * Manual testing has been done. * Boats have bounced back when collides with other boats, mark or boundary. #story[1117] #pair[hyi25, zyt10] See merge request !60
This commit is contained in:
@@ -6,8 +6,12 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
import seng302.gameServer.server.messages.BoatAction;
|
import seng302.gameServer.server.messages.BoatAction;
|
||||||
import seng302.gameServer.server.messages.BoatStatus;
|
import seng302.gameServer.server.messages.BoatStatus;
|
||||||
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
||||||
@@ -16,6 +20,7 @@ import seng302.gameServer.server.messages.Message;
|
|||||||
import seng302.gameServer.server.messages.RoundingBoatStatus;
|
import seng302.gameServer.server.messages.RoundingBoatStatus;
|
||||||
import seng302.gameServer.server.messages.YachtEventCodeMessage;
|
import seng302.gameServer.server.messages.YachtEventCodeMessage;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
|
import seng302.model.Limit;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.PolarTable;
|
import seng302.model.PolarTable;
|
||||||
import seng302.model.ServerYacht;
|
import seng302.model.ServerYacht;
|
||||||
@@ -23,6 +28,7 @@ import seng302.model.mark.CompoundMark;
|
|||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.MarkOrder;
|
import seng302.model.mark.MarkOrder;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
import seng302.utilities.XMLParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Static class to hold information about the current state of the game (model)
|
* A Static class to hold information about the current state of the game (model)
|
||||||
@@ -33,6 +39,7 @@ public class GameState implements Runnable {
|
|||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface NewMessageListener {
|
interface NewMessageListener {
|
||||||
|
|
||||||
void notify(Message message);
|
void notify(Message message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +66,7 @@ public class GameState implements Runnable {
|
|||||||
private static MarkOrder markOrder;
|
private static MarkOrder markOrder;
|
||||||
private static long startTime;
|
private static long startTime;
|
||||||
private static Set<Mark> marks;
|
private static Set<Mark> marks;
|
||||||
|
private static List<Limit> courseLimit;
|
||||||
|
|
||||||
private static List<NewMessageListener> markListeners;
|
private static List<NewMessageListener> markListeners;
|
||||||
|
|
||||||
@@ -81,7 +89,7 @@ public class GameState implements Runnable {
|
|||||||
yachts = new HashMap<>();
|
yachts = new HashMap<>();
|
||||||
players = new ArrayList<>();
|
players = new ArrayList<>();
|
||||||
GameState.hostIpAddress = hostIpAddress;
|
GameState.hostIpAddress = hostIpAddress;
|
||||||
;
|
|
||||||
currentStage = GameStages.LOBBYING;
|
currentStage = GameStages.LOBBYING;
|
||||||
isRaceStarted = false;
|
isRaceStarted = false;
|
||||||
//set this when game stage changes to prerace
|
//set this when game stage changes to prerace
|
||||||
@@ -95,6 +103,22 @@ public class GameState implements Runnable {
|
|||||||
new Thread(this, "GameState").start(); //Run the auto updates on the game state
|
new Thread(this, "GameState").start(); //Run the auto updates on the game state
|
||||||
|
|
||||||
marks = new MarkOrder().getAllMarks();
|
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);
|
||||||
|
}
|
||||||
|
courseLimit = XMLParser.parseRace(document).getCourseLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getHostIpAddress() {
|
public static String getHostIpAddress() {
|
||||||
@@ -244,7 +268,7 @@ public class GameState implements Runnable {
|
|||||||
yacht.runAutoPilot();
|
yacht.runAutoPilot();
|
||||||
yacht.updateLocation(timeInterval);
|
yacht.updateLocation(timeInterval);
|
||||||
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
||||||
checkForCollision(yacht);
|
checkCollision(yacht);
|
||||||
checkForLegProgression(yacht);
|
checkForLegProgression(yacht);
|
||||||
raceFinished = false;
|
raceFinished = false;
|
||||||
}
|
}
|
||||||
@@ -255,9 +279,28 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the yacht has crossed the course limit
|
||||||
|
*
|
||||||
|
* @param yacht the yacht to be tested
|
||||||
|
* @return a boolean value of if there is a boundary collision
|
||||||
|
*/
|
||||||
|
private static Boolean checkBoundaryCollision(ServerYacht yacht) {
|
||||||
|
for (int i = 0; i < courseLimit.size() - 1; i++) {
|
||||||
|
if (GeoUtility.checkCrossedLine(courseLimit.get(i), courseLimit.get(i + 1),
|
||||||
|
yacht.getLastLocation(), yacht.getLocation()) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GeoUtility.checkCrossedLine(courseLimit.get(courseLimit.size() - 1), courseLimit.get(0),
|
||||||
|
yacht.getLastLocation(), yacht.getLocation()) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void checkForCollision(ServerYacht serverYacht) {
|
public static void checkCollision(ServerYacht serverYacht) {
|
||||||
ServerYacht collidedYacht = checkCollision(serverYacht);
|
ServerYacht collidedYacht = checkYachtCollision(serverYacht);
|
||||||
if (collidedYacht != null) {
|
if (collidedYacht != null) {
|
||||||
GeoPoint originalLocation = serverYacht.getLocation();
|
GeoPoint originalLocation = serverYacht.getLocation();
|
||||||
serverYacht.setLocation(
|
serverYacht.setLocation(
|
||||||
@@ -276,7 +319,7 @@ public class GameState implements Runnable {
|
|||||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Mark collidedMark = markCollidedWith(serverYacht);
|
Mark collidedMark = checkMarkCollision(serverYacht);
|
||||||
if (collidedMark != null) {
|
if (collidedMark != null) {
|
||||||
serverYacht.setLocation(
|
serverYacht.setLocation(
|
||||||
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
||||||
@@ -287,6 +330,17 @@ public class GameState implements Runnable {
|
|||||||
notifyMessageListeners(
|
notifyMessageListeners(
|
||||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
);
|
);
|
||||||
|
} else if (checkBoundaryCollision(serverYacht)) {
|
||||||
|
serverYacht.setLocation(
|
||||||
|
calculateBounceBack(serverYacht, serverYacht.getLocation(),
|
||||||
|
BOUNCE_DISTANCE_YACHT)
|
||||||
|
);
|
||||||
|
serverYacht.setCurrentVelocity(
|
||||||
|
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||||
|
);
|
||||||
|
notifyMessageListeners(
|
||||||
|
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,6 +407,7 @@ public class GameState implements Runnable {
|
|||||||
/**
|
/**
|
||||||
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
||||||
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
||||||
|
*
|
||||||
* @param yacht the current yacht to check for progression
|
* @param yacht the current yacht to check for progression
|
||||||
*/
|
*/
|
||||||
private void checkForLegProgression(ServerYacht yacht) {
|
private void checkForLegProgression(ServerYacht yacht) {
|
||||||
@@ -516,7 +571,7 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Mark markCollidedWith(ServerYacht yacht) {
|
private static Mark checkMarkCollision(ServerYacht yacht) {
|
||||||
Set<Mark> marksInRace = GameState.getMarks();
|
Set<Mark> marksInRace = GameState.getMarks();
|
||||||
for (Mark mark : marksInRace) {
|
for (Mark mark : marksInRace) {
|
||||||
if (GeoUtility.getDistance(yacht.getLocation(), mark)
|
if (GeoUtility.getDistance(yacht.getLocation(), mark)
|
||||||
@@ -532,12 +587,14 @@ public class GameState implements Runnable {
|
|||||||
*
|
*
|
||||||
* @return The boats new position
|
* @return The boats new position
|
||||||
*/
|
*/
|
||||||
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith, Double bounceDistance) {
|
private static GeoPoint calculateBounceBack(ServerYacht yacht, GeoPoint collidedWith,
|
||||||
Double heading = GeoUtility.getBearing(yacht.getLocation(), collidedWith);
|
Double bounceDistance) {
|
||||||
|
Double heading = GeoUtility.getBearing(yacht.getLastLocation(), collidedWith);
|
||||||
// Invert heading
|
// Invert heading
|
||||||
heading -= 180;
|
heading -= 180;
|
||||||
Integer newHeading = Math.floorMod(heading.intValue(), 360);
|
Integer newHeading = Math.floorMod(heading.intValue(), 360);
|
||||||
return GeoUtility.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
|
return GeoUtility
|
||||||
|
.getGeoCoordinate(yacht.getLocation(), newHeading.doubleValue(), bounceDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -546,11 +603,12 @@ public class GameState implements Runnable {
|
|||||||
*
|
*
|
||||||
* @return yacht to compare to all other yachts.
|
* @return yacht to compare to all other yachts.
|
||||||
*/
|
*/
|
||||||
private static ServerYacht checkCollision(ServerYacht yacht) {
|
private static ServerYacht checkYachtCollision(ServerYacht yacht) {
|
||||||
|
|
||||||
for (ServerYacht otherYacht : GameState.getYachts().values()) {
|
for (ServerYacht otherYacht : GameState.getYachts().values()) {
|
||||||
if (otherYacht != yacht) {
|
if (otherYacht != yacht) {
|
||||||
Double distance = GeoUtility.getDistance(otherYacht.getLocation(), yacht.getLocation());
|
Double distance = GeoUtility
|
||||||
|
.getDistance(otherYacht.getLocation(), yacht.getLocation());
|
||||||
if (distance < YACHT_COLLISION_DISTANCE) {
|
if (distance < YACHT_COLLISION_DISTANCE) {
|
||||||
return otherYacht;
|
return otherYacht;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class ServerYacht extends Observable {
|
|||||||
this.country = country;
|
this.country = country;
|
||||||
this.sailIn = false;
|
this.sailIn = false;
|
||||||
this.isAuto = false;
|
this.isAuto = false;
|
||||||
this.location = new GeoPoint(57.670341, 11.826856);
|
this.location = new GeoPoint(57.67046, 11.83751);
|
||||||
this.lastLocation = location;
|
this.lastLocation = location;
|
||||||
this.heading = 120.0; //In degrees
|
this.heading = 120.0; //In degrees
|
||||||
this.currentVelocity = 0d; //in mms-1
|
this.currentVelocity = 0d; //in mms-1
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import org.junit.Test;
|
|||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
|
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
|
* Test update function in Yacht.java to make sure yacht will not be collide each other within 25.0
|
||||||
* meters.
|
* meters.
|
||||||
@@ -37,7 +39,7 @@ public class UpdateYachtTest {
|
|||||||
if (!yacht1.getSailIn()) {
|
if (!yacht1.getSailIn()) {
|
||||||
yacht1.toggleSailIn();
|
yacht1.toggleSailIn();
|
||||||
}
|
}
|
||||||
GameState.checkForCollision(yacht1);
|
checkCollision(yacht1);
|
||||||
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
|
double moved = GeoUtility.getDistance(yacht1.getLocation(), geoPoint1);
|
||||||
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
|
Assert.assertEquals(GameState.BOUNCE_DISTANCE_YACHT, moved, 0.1);
|
||||||
}
|
}
|
||||||
@@ -54,14 +56,14 @@ public class UpdateYachtTest {
|
|||||||
if (!yacht1.getSailIn()) {
|
if (!yacht1.getSailIn()) {
|
||||||
yacht1.toggleSailIn();
|
yacht1.toggleSailIn();
|
||||||
}
|
}
|
||||||
GameState.checkForCollision(yacht1);
|
checkCollision(yacht1);
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
|
GameState.YACHT_COLLISION_DISTANCE < GeoUtility.getDistance(geoPoint1, geoPoint2
|
||||||
)
|
)
|
||||||
); //Check that yachts are actually far enough apart for no collision.
|
); //Check that yachts are actually far enough apart for no collision.
|
||||||
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 0.001);
|
Assert.assertEquals(geoPoint1.getLat(), yacht1.getLocation().getLat(), 1.001);
|
||||||
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 0.001);
|
Assert.assertEquals(geoPoint1.getLng(), yacht1.getLocation().getLng(), 1.001);
|
||||||
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 0.001);
|
Assert.assertEquals(geoPoint2.getLat(), yacht1.getLocation().getLat(), 1.001);
|
||||||
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 0.001);
|
Assert.assertEquals(geoPoint2.getLng(), yacht1.getLocation().getLng(), 1.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user