Implemented wind walker algorithm. Refactored some GameState updating logic to allow for better token logic integration

GameState: Moved all token logic into its own function startPoint so that it is dsijoint from other updating logic
GameState: Implemented wind walker algorithm.
GameState: Changed Generic 'speedMultiplier' to 'serverSpeedMultiplier' to make it obviously disjoint from a boats speed multiplier
MessageFactory: Moved some found message creation (Chatter Message)  server side into MessageFactory that wasnt already there
ServerYacht: Added a speed multiplier and a handling multiplier to the serveryacht class that is set and reset upon powerup / down

#story[1293]
This commit is contained in:
William Muir
2017-09-22 23:44:03 +12:00
parent a3c555d5fe
commit 061e49bab9
5 changed files with 158 additions and 51 deletions
+123 -41
View File
@@ -46,12 +46,14 @@ public class GameState implements Runnable {
private static final Double BOUNCE_DISTANCE_MARK = 20.0; private static final Double BOUNCE_DISTANCE_MARK = 20.0;
public static final Double BOUNCE_DISTANCE_YACHT = 30.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
private static final Double COLLISION_VELOCITY_PENALTY = 0.3; private static final Double COLLISION_VELOCITY_PENALTY = 0.3;
private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; private static final Integer VELOCITY_BOOST_MULTIPLIER = 2;
private static final Integer HANDLING_BOOST_MULTIPLIER = 2;
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
private static Double windSpeed; private static Double windSpeed;
private static Double speedMultiplier = 1d; private static Double serverSpeedMultiplier = 1d;
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat. private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
private static Boolean playerHasLeftFlag; private static Boolean playerHasLeftFlag;
@@ -85,7 +87,7 @@ public class GameState implements Runnable {
GameState.hostIpAddress = hostIpAddress; GameState.hostIpAddress = hostIpAddress;
customizationFlag = false; customizationFlag = false;
playerHasLeftFlag = false; playerHasLeftFlag = false;
speedMultiplier = 1.0; serverSpeedMultiplier = 1.0;
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
@@ -283,9 +285,11 @@ public class GameState implements Runnable {
tokensInPlay.clear(); tokensInPlay.clear();
//Get a random token location with random type //Get a random token location with random type
Token token = allTokens.get(random.nextInt(allTokens.size())); Token token = allTokens.get(random.nextInt(allTokens.size() - 1) + 1);
token.assignRandomType(); token.assignRandomType();
logger.debug("Spawned token of type " + token.getTokenType());
tokensInPlay.add(token); tokensInPlay.add(token);
} }
@@ -308,10 +312,10 @@ public class GameState implements Runnable {
} }
for (ServerYacht yacht : yachts.values()) { for (ServerYacht yacht : yachts.values()) {
updateVelocity(yacht); updateVelocity(yacht);
checkPowerUpTimeout(yacht);
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
checkCollision(yacht); checkCollision(yacht);
preformTokenUpdates(yacht); //This update must always be done lsat
if (yacht.getBoatStatus() != BoatStatus.FINISHED) { if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
checkForLegProgression(yacht); checkForLegProgression(yacht);
raceFinished = false; raceFinished = false;
@@ -324,15 +328,79 @@ public class GameState implements Runnable {
} }
private void checkPowerUpTimeout(ServerYacht yacht) { /**
* All token functionality entry points is taken care of here. So can be disabled and enabled
* easily
*
* @param yacht The yacht to perform token checks on
*/
private void preformTokenUpdates(ServerYacht yacht) {
checkTokenPickUp(yacht);
if (yacht.getPowerUp() != null) { if (yacht.getPowerUp() != null) {
switch (yacht.getPowerUp()) {
case WIND_WALKER:
windWalk(yacht);
break;
case BUMPER:
break;
}
checkPowerUpTimeout(yacht);
}
}
/**
* Checks how long a powerup has been active for. If it has exceeded its timeout, it powers the
* yacht down. WARNING. Do not call if the yacht does not have an active power up. Check with
* yacht.getPowerup != null first
*
* @param yacht The yacht to check to power down
*/
private void checkPowerUpTimeout(ServerYacht yacht) {
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp()
.getTimeout()) { .getTimeout()) {
yacht.powerDown(); yacht.powerDown();
sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired"); String logMessage = yacht.getBoatName() + "'s power-up token expired";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
} }
} }
/**
* This function changes the wind to be at an angle that causes the yacht in question to be at
* VMG.
*
* @param yacht The yacht to fix the wind for
*/
private void windWalk(ServerYacht yacht) {
HashMap<Double, Double> upwindOptimal = PolarTable.getOptimalUpwindVMG(windSpeed);
Double optimalAngle = null;
for (Double windAngle : upwindOptimal.keySet()) {
optimalAngle = windAngle;
}
Double heading = yacht.getHeading();
if (heading < windDirection) {
Double diff = Math.abs(optimalAngle - (windDirection - heading));
if (windDirection - heading < optimalAngle) {
windDirection = (double) Math.floorMod(Math.round(windDirection + diff), 360L);
} else {
windDirection = (double) Math.floorMod(Math.round(windDirection - diff), 360L);
}
} else {
Double diff = Math.abs(optimalAngle - (heading - windDirection));
if (heading - windDirection < optimalAngle) {
windDirection = (double) Math.floorMod(Math.round(windDirection - diff), 360L);
} else {
windDirection = (double) Math.floorMod(Math.round(windDirection + diff), 360L);
}
}
} }
@@ -357,19 +425,53 @@ public class GameState implements Runnable {
} }
/** /**
* Checks all tokensInPlay to see if a yacht has picked one up * Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up
* @return Token which was collided with * in the appropriate way
* @param serverYacht The yacht to check for collision with a token * @param yacht The yacht to check for collision with a token
*/ */
private static Token checkTokenPickUp(ServerYacht serverYacht) { private void checkTokenPickUp(ServerYacht yacht) {
Token collidedToken = null;
for (Token token : tokensInPlay) { for (Token token : tokensInPlay) {
Double distance = GeoUtility.getDistance(token, serverYacht.getLocation()); Double distance = GeoUtility.getDistance(token, yacht.getLocation());
if (distance < YACHT_COLLISION_DISTANCE) { if (distance < YACHT_COLLISION_DISTANCE) {
return token; collidedToken = token;
} }
} }
return null; if (collidedToken != null) {
tokensInPlay.remove(collidedToken);
if (collidedToken.getTokenType() == TokenType.RANDOM) {
collidedToken.realiseRandom();
}
TokenType tokenType = collidedToken.getTokenType();
switch (tokenType) {
case BOOST:
yacht.setSpeedMultiplier(VELOCITY_BOOST_MULTIPLIER);
break;
case BUMPER:
// TODO: 22/09/17 wmu16
break;
case HANDLING:
yacht.setHandlingMultiplier(HANDLING_BOOST_MULTIPLIER);
break;
case WIND_WALKER:
// TODO: 22/09/17 wmu16
break;
}
yacht.powerUp(tokenType);
String logMessage =
yacht.getBoatName() + " has picked up a " + collidedToken.getTokenType().getName()
+ " token";
notifyMessageListeners(
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(MessageFactory.makePickupMessage(yacht, collidedToken));
logger.debug("Yacht: " + yacht.getShortName() + " got powerup " + collidedToken
.getTokenType());
}
} }
@@ -388,7 +490,6 @@ public class GameState implements Runnable {
//Yacht Collision //Yacht Collision
ServerYacht collidedYacht = checkYachtCollision(serverYacht); ServerYacht collidedYacht = checkYachtCollision(serverYacht);
Mark collidedMark = checkMarkCollision(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht);
Token collidedToken = checkTokenPickUp(serverYacht);
if (collidedYacht != null) { if (collidedYacht != null) {
GeoPoint originalLocation = serverYacht.getLocation(); GeoPoint originalLocation = serverYacht.getLocation();
@@ -431,34 +532,14 @@ public class GameState implements Runnable {
); );
notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht)); notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht));
} }
//Token Collision
if (collidedToken != null) {
if (collidedToken.getTokenType() == TokenType.RANDOM) {
collidedToken.realiseRandom();
}
sendServerMessage(serverYacht.getSourceId(),
serverYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType()
.getName() + " token");
tokensInPlay.remove(collidedToken);
serverYacht.powerUp(collidedToken.getTokenType());
logger.debug("Yacht: " + serverYacht.getShortName() + " got powerup " + collidedToken
.getTokenType());
notifyMessageListeners(MessageFactory.getRaceXML());
notifyMessageListeners(MessageFactory.makePickupMessage(serverYacht, collidedToken));
}
} }
private void updateVelocity(ServerYacht yacht) { private void updateVelocity(ServerYacht yacht) {
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier; Double maxBoatSpeed =
if (yacht.getPowerUp() != null) { GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht.getSpeedMultiplier();
if (yacht.getPowerUp().equals(TokenType.BOOST)) {
maxBoatSpeed *= VELOCITY_BOOST_MULTIPLIER;
}
}
Double currentVelocity = yacht.getCurrentVelocity(); Double currentVelocity = yacht.getCurrentVelocity();
// TODO: 15/08/17 remove magic numbers from these equations. // TODO: 15/08/17 remove magic numbers from these equations.
@@ -700,6 +781,7 @@ public class GameState implements Runnable {
String name = new String(customizeData); String name = new String(customizeData);
playerYacht.setBoatName(name); playerYacht.setBoatName(name);
} else if (requestType.equals(CustomizeRequestType.COLOR)) { } else if (requestType.equals(CustomizeRequestType.COLOR)) {
//This low level stuff shouldnt be here alistair! In fact no logic LIKE THIS should! - wmu16
int red = customizeData[0] & 0xFF; int red = customizeData[0] & 0xFF;
int green = customizeData[1] & 0xFF; int green = customizeData[1] & 0xFF;
int blue = customizeData[2] & 0xFF; int blue = customizeData[2] & 0xFF;
@@ -799,7 +881,7 @@ public class GameState implements Runnable {
switch (words[2].trim()) { switch (words[2].trim()) {
case "/speed": case "/speed":
try { try {
setSpeedMultiplier(Double.valueOf(words[3])); setServerSpeedMultiplier(Double.valueOf(words[3]));
sendServerMessage(chatterMessage.getMessage_type(), sendServerMessage(chatterMessage.getMessage_type(),
"Speed modifier set to x" + words[3]); "Speed modifier set to x" + words[3]);
} catch (Exception e) { } catch (Exception e) {
@@ -866,11 +948,11 @@ public class GameState implements Runnable {
currentStage = GameStages.FINISHED; currentStage = GameStages.FINISHED;
} }
public static void setSpeedMultiplier (double multiplier) { public static void setServerSpeedMultiplier(double multiplier) {
speedMultiplier = multiplier; serverSpeedMultiplier = multiplier;
} }
public static double getSpeedMultiplier () { public static double getServerSpeedMultiplier() {
return speedMultiplier; return serverSpeedMultiplier;
} }
} }
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import seng302.gameServer.messages.BoatLocationMessage; import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.BoatSubMessage; import seng302.gameServer.messages.BoatSubMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.RaceStartNotificationType; import seng302.gameServer.messages.RaceStartNotificationType;
import seng302.gameServer.messages.RaceStartStatusMessage; import seng302.gameServer.messages.RaceStartStatusMessage;
import seng302.gameServer.messages.RaceStatus; import seng302.gameServer.messages.RaceStatus;
@@ -157,4 +158,8 @@ public class MessageFactory {
} }
return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType); return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType);
} }
public static ChatterMessage makeChatterMessage(Integer messageType, String message) {
return new ChatterMessage(messageType, "SERVER: " + message);
}
} }
+23 -1
View File
@@ -56,6 +56,8 @@ public class ServerYacht {
//PowerUp //PowerUp
private TokenType powerUp; private TokenType powerUp;
private Long powerUpStartTime; private Long powerUpStartTime;
private Integer speedMultiplier;
private Integer handlingMultiplier;
public ServerYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName, public ServerYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName,
@@ -77,6 +79,8 @@ public class ServerYacht {
this.legNumber = 0; this.legNumber = 0;
this.boatColor = Colors.getColor(sourceId - 1); this.boatColor = Colors.getColor(sourceId - 1);
this.powerUp = null; this.powerUp = null;
this.speedMultiplier = 1;
this.handlingMultiplier = 1;
this.hasEnteredRoundingZone = false; this.hasEnteredRoundingZone = false;
this.hasPassedLine = false; this.hasPassedLine = false;
@@ -114,6 +118,8 @@ public class ServerYacht {
public void powerDown() { public void powerDown() {
this.powerUp = null; this.powerUp = null;
this.speedMultiplier = 1;
this.handlingMultiplier = 1;
} }
public Long getPowerUpStartTime() { public Long getPowerUpStartTime() {
@@ -130,7 +136,7 @@ public class ServerYacht {
* @param amount the amount by which to adjust the boat heading. * @param amount the amount by which to adjust the boat heading.
*/ */
public void adjustHeading(Double amount) { public void adjustHeading(Double amount) {
Double newVal = heading + amount; Double newVal = heading + amount * handlingMultiplier;
lastHeading = heading; lastHeading = heading;
heading = (double) Math.floorMod(newVal.longValue(), 360L); heading = (double) Math.floorMod(newVal.longValue(), 360L);
} }
@@ -429,4 +435,20 @@ public class ServerYacht {
public BoatMeshType getBoatType() { public BoatMeshType getBoatType() {
return boatType; return boatType;
} }
public Integer getSpeedMultiplier() {
return speedMultiplier;
}
public void setSpeedMultiplier(Integer speedMultiplier) {
this.speedMultiplier = speedMultiplier;
}
public Integer getHandlingMultiplier() {
return handlingMultiplier;
}
public void setHandlingMultiplier(Integer handlingMultiplier) {
this.handlingMultiplier = handlingMultiplier;
}
} }
@@ -39,6 +39,4 @@ public class Token extends GeoPoint {
tokenTypeList.remove(TokenType.RANDOM); tokenTypeList.remove(TokenType.RANDOM);
tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size())); tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size()));
} }
} }
@@ -110,7 +110,7 @@ public class ChatCommandsTest {
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
Assert.assertEquals(5.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(5.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); mst.terminate();
try { try {
Thread.sleep(200); Thread.sleep(200);
@@ -150,7 +150,7 @@ public class ChatCommandsTest {
ie.printStackTrace(); ie.printStackTrace();
} }
mst.terminate(); mst.terminate();
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
@@ -194,7 +194,7 @@ public class ChatCommandsTest {
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
ie.printStackTrace(); ie.printStackTrace();
} }
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001);
mst.terminate(); mst.terminate();
host.setSocketToClose(); host.setSocketToClose();
client.setSocketToClose(); client.setSocketToClose();