Created Boat bumper logic. Refactored logic for powering up / dpwn

YachtEventType: Added some new events, a generic power down event and a bumper_crash event for an affected boat
GameState: Implemented boat bumper logic
MessageFactory: Made new messages for powerdown and status effect
ClientYacht: Had to create another powerDown functional interface to inform the race view controller when to turn off the icon
RaceViewController/GameClient: Now waits for a message about powering down before turning off rather than waiting time client side

#story[1293]
This commit is contained in:
William Muir
2017-09-23 13:23:16 +12:00
parent ecb3d4ecbf
commit 8c7f9a878d
7 changed files with 147 additions and 30 deletions
+49 -17
View File
@@ -1,5 +1,6 @@
package seng302.gameServer; package seng302.gameServer;
import com.sun.corba.se.spi.activation.Server;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -59,6 +60,7 @@ public class GameState implements Runnable {
//Powerup Constants //Powerup Constants
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 final Integer HANDLING_BOOST_MULTIPLIER = 2;
private static final Long BUMPER_DISABLE_TIME = 5_000L;
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
@@ -279,7 +281,7 @@ public class GameState implements Runnable {
spawnNewToken(); spawnNewToken();
notifyMessageListeners(MessageFactory.getRaceXML()); notifyMessageListeners(MessageFactory.getRaceXML());
} }
}, 0, 60000); }, 0, 15_000);
} }
// TODO: 29/08/17 wmu16 - This sort of update should be in game state // TODO: 29/08/17 wmu16 - This sort of update should be in game state
@@ -351,13 +353,14 @@ public class GameState implements Runnable {
* Randomly select a subset of tokensInPlay from a pre defined superset * Randomly select a subset of tokensInPlay from a pre defined superset
* Broadasts a new race status message to show this update * Broadasts a new race status message to show this update
*/ */
public static void spawnNewToken() { private void spawnNewToken() {
Random random = new Random(); Random random = new Random();
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()));
token.assignRandomType(); // token.assignRandomType();
token.assignType(TokenType.BUMPER);
logger.debug("Spawned token of type " + token.getTokenType()); logger.debug("Spawned token of type " + token.getTokenType());
@@ -383,8 +386,9 @@ public class GameState implements Runnable {
updateVelocity(yacht); updateVelocity(yacht);
yacht.runAutoPilot(); yacht.runAutoPilot();
yacht.updateLocation(timeInterval); yacht.updateLocation(timeInterval);
preformTokenUpdates(
yacht); //This update must be done before collision. Sorry sorta hacky rn.
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;
@@ -405,38 +409,66 @@ public class GameState implements Runnable {
*/ */
private void preformTokenUpdates(ServerYacht yacht) { private void preformTokenUpdates(ServerYacht yacht) {
checkTokenPickUp(yacht); checkTokenPickUp(yacht);
checkPowerUpTimeout(yacht);
TokenType powerUp = yacht.getPowerUp();
if (yacht.getPowerUp() != null) { if (powerUp != null) {
switch (yacht.getPowerUp()) { switch (powerUp) {
case WIND_WALKER: case WIND_WALKER:
windWalk(yacht); windWalk(yacht);
break; break;
case BUMPER: case BUMPER:
ServerYacht collidedYacht = checkYachtCollision(yacht);
if (collidedYacht != null) {
yacht.powerDown();
boatTempShutDown(collidedYacht);
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
notifyMessageListeners(
MessageFactory.makeStatusEffectMessage(yacht, powerUp));
}
break; break;
} }
checkPowerUpTimeout(yacht);
} }
}
// TODO: 23/09/17 wmu16 - This is a hacky way to have the boat power down. Need some sort of separation between token and status effect :/
/**
* Disables the given boat for BUMPER_DISABLE_TIME ms.
*
* @param yacht The yacht to disable
*/
private void boatTempShutDown(ServerYacht yacht) {
yacht.setSpeedMultiplier(0);
Timer shutDownTimer = new Timer("Shutdown Timer");
shutDownTimer.schedule(new TimerTask() {
@Override
public void run() {
yacht.powerDown(); //Note this actually resets the boat to normal.
}
}, BUMPER_DISABLE_TIME);
} }
/** /**
* Checks how long a powerup has been active for. If it has exceeded its timeout, it powers the * 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 down.
* yacht.getPowerup != null first
* *
* @param yacht The yacht to check to power down * @param yacht The yacht to check to power down
*/ */
private void checkPowerUpTimeout(ServerYacht yacht) { private void checkPowerUpTimeout(ServerYacht yacht) {
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() if (yacht.getPowerUp() != null) {
.getTimeout()) { if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp()
yacht.powerDown(); .getTimeout()) {
String logMessage = yacht.getBoatName() + "'s power-up token expired"; String logMessage =
notifyMessageListeners( yacht.getBoatName() + "'s " + yacht.getPowerUp().getName() + " expired";
MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); notifyMessageListeners(
logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage));
notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht));
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
yacht.powerDown();
}
} }
} }
@@ -137,6 +137,14 @@ public class MessageFactory {
return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION); return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION);
} }
/**
* Constructs a message to be sent out whenever a yacht picks up a boost
*
* @param serverYacht The yacht that has picked up a power up
* @param token The token which they picked up
* @return The corresponding YachtEventCodeMessage
*/
public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) { public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) {
YachtEventType yachtEventType = null; YachtEventType yachtEventType = null;
switch (token.getTokenType()) { switch (token.getTokenType()) {
@@ -159,6 +167,37 @@ public class MessageFactory {
return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType); return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType);
} }
/**
* Constructs a message representing a certain buff / debuff for a given yacht. For now this is
* just for the bumper debuff so the affected boat is aware that it has been crashed. This could
* however be extended to render affects for all boats given a certain debuff.
*
* @param yacht The yacht affected by some status
* @param token The token indicating what status they have
* @return A YachtEventCodeMessage
*/
public static YachtEventCodeMessage makeStatusEffectMessage(ServerYacht yacht,
TokenType token) {
YachtEventType yachtEventType = null;
switch (token) {
case BUMPER:
yachtEventType = YachtEventType.BUMPER_CRASH;
break;
}
return new YachtEventCodeMessage(yacht.getSourceId(), yachtEventType);
}
/**
* Constructs a message to be sent out when a given yacht powers down (From a boost of any type)
*
* @param yacht The yacht that is powering down
* @return A YachtEventCodeMessage representing this action
*/
public static YachtEventCodeMessage makePowerDownMessage(ServerYacht yacht) {
return new YachtEventCodeMessage(yacht.getSourceId(), YachtEventType.POWER_DOWN);
}
public static ChatterMessage makeChatterMessage(Integer messageType, String message) { public static ChatterMessage makeChatterMessage(Integer messageType, String message) {
return new ChatterMessage(messageType, "SERVER: " + message); return new ChatterMessage(messageType, "SERVER: " + message);
} }
@@ -9,7 +9,10 @@ public enum YachtEventType {
TOKEN_BUMPER(35), TOKEN_BUMPER(35),
TOKEN_HANDLING(36), TOKEN_HANDLING(36),
TOKEN_WIND_WALKER(37), TOKEN_WIND_WALKER(37),
TOKEN_RANDOM(38); TOKEN_RANDOM(38),
POWER_DOWN(39),
BUMPER_CRASH(40);
private int code; private int code;
@@ -39,11 +39,22 @@ public class ClientYacht extends Observable {
void notifyRounding(ClientYacht yacht, int legNumber); void notifyRounding(ClientYacht yacht, int legNumber);
} }
//This notifies RaceViewController so it can display icon - wmu16
@FunctionalInterface @FunctionalInterface
public interface PowerUpListener { public interface PowerUpListener {
void notifyPowerUp(ClientYacht yacht, TokenType tokenType); void notifyPowerUp(ClientYacht yacht, TokenType tokenType);
} }
//This notifies RaceViewController so it can remove token icon - wmu16
@FunctionalInterface
public interface PowerDownListener {
void notifyPowerDown(ClientYacht yacht);
}
private Logger logger = LoggerFactory.getLogger(ClientYacht.class); private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
@@ -70,6 +81,8 @@ public class ClientYacht extends Observable {
private List<YachtLocationListener> locationListeners = new ArrayList<>(); private List<YachtLocationListener> locationListeners = new ArrayList<>();
private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>(); private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>();
private List<PowerUpListener> powerUpListeners = new ArrayList<>(); private List<PowerUpListener> powerUpListeners = new ArrayList<>();
private List<PowerDownListener> powerDownListeners = new ArrayList<>();
private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper();
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
@@ -211,6 +224,13 @@ public class ClientYacht extends Observable {
this.position = position; this.position = position;
} }
public void powerDown() {
this.powerUp = null;
for (PowerDownListener listener : powerDownListeners) {
listener.notifyPowerDown(this);
}
}
public void setPowerUp(TokenType tokenType) { public void setPowerUp(TokenType tokenType) {
this.powerUp = tokenType; this.powerUp = tokenType;
for (PowerUpListener listener : powerUpListeners) { for (PowerUpListener listener : powerUpListeners) {
@@ -295,6 +315,10 @@ public class ClientYacht extends Observable {
powerUpListeners.add(listener); powerUpListeners.add(listener);
} }
public void addPowerDownListener(PowerDownListener listener) {
powerDownListeners.add(listener);
}
public void removeMarkRoundingListener(MarkRoundingListener listener) { public void removeMarkRoundingListener(MarkRoundingListener listener) {
markRoundingListeners.remove(listener); markRoundingListeners.remove(listener);
} }
@@ -39,4 +39,11 @@ 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()));
} }
/**
* Exists for testing purposes only
*/
public void assignType(TokenType tokenType) {
this.tokenType = tokenType;
}
} }
@@ -410,8 +410,15 @@ public class GameClient {
* @param yachtEventData The YachtEvent data packet * @param yachtEventData The YachtEvent data packet
*/ */
private void processYachtEvent(YachtEventData yachtEventData) { private void processYachtEvent(YachtEventData yachtEventData) {
ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue());
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
showCollisionAlert(yachtEventData); showCollisionAlert(yachtEventData);
} else if (yachtEventData.getEventId() == YachtEventType.POWER_DOWN.getCode()) {
thisYacht.powerDown();
Sounds.playTokenPickupSound(); // TODO: 23/09/17 This should be power down sound
} else if (yachtEventData.getEventId() == YachtEventType.BUMPER_CRASH.getCode()) {
// TODO: 23/09/17 notify the client that the yacht has been disabled
} else { } else {
TokenType tokenType = null; TokenType tokenType = null;
if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) {
@@ -427,7 +434,7 @@ public class GameClient {
} }
showTokenPickUp(tokenType); showTokenPickUp(tokenType);
allBoatsMap.get(yachtEventData.getSubjectId().intValue()).setPowerUp(tokenType); thisYacht.setPowerUp(tokenType);
} }
} }
@@ -48,6 +48,7 @@ import javafx.scene.shape.Polyline;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import javax.swing.ImageIcon;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.ClientYacht.PowerUpListener; import seng302.model.ClientYacht.PowerUpListener;
import seng302.model.RaceState; import seng302.model.RaceState;
@@ -140,6 +141,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private JFXDialog finishScreenDialog; private JFXDialog finishScreenDialog;
private FinishDialogController finishDialogController; private FinishDialogController finishDialogController;
//Icon stuff
private Timer blinkingTimer = new Timer();
private ImageView iconToDisplay;
public void initialize() { public void initialize() {
Sounds.stopMusic(); Sounds.stopMusic();
Sounds.playRaceMusic(); Sounds.playRaceMusic();
@@ -253,6 +258,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}); });
player.addPowerUpListener(this::displayPowerUpIcon); player.addPowerUpListener(this::displayPowerUpIcon);
player.addPowerDownListener(this::removeIcon);
updateOrder(raceState.getPlayerPositions()); updateOrder(raceState.getPlayerPositions());
gameView = new GameView3D(); gameView = new GameView3D();
@@ -301,7 +307,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
*/ */
private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) { private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) {
if (yacht == player) { if (yacht == player) {
final ImageView iconToDisplay;
switch (tokenType) { switch (tokenType) {
case BOOST: case BOOST:
@@ -324,7 +329,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
iconToDisplay.setVisible(true); iconToDisplay.setVisible(true);
//Start blinking icon towards end //Start blinking icon towards end
Timer blinkingTimer = new Timer(); if (blinkingTimer != null) {
blinkingTimer.cancel();
}
blinkingTimer = new Timer("Blinking Timer");
blinkingTimer.schedule(new TimerTask() { blinkingTimer.schedule(new TimerTask() {
Boolean isVisible = true; Boolean isVisible = true;
@@ -334,16 +342,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
iconToDisplay.setVisible(isVisible); iconToDisplay.setVisible(isVisible);
} }
}, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD); }, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD);
}
}
//Turn icon off after the time out public void removeIcon(ClientYacht yacht) {
Timer switchOffTimer = new Timer(); if (yacht == player) {
switchOffTimer.schedule(new TimerTask() { blinkingTimer.cancel();
@Override iconToDisplay.setVisible(false);
public void run() {
blinkingTimer.cancel();
iconToDisplay.setVisible(false);
}
}, tokenType.getTimeout());
} }
} }