From 8c7f9a878db16e5042ab4a8d234d8dbdbaf07956 Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 13:23:16 +1200 Subject: [PATCH] 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] --- .../java/seng302/gameServer/GameState.java | 66 ++++++++++++++----- .../seng302/gameServer/MessageFactory.java | 39 +++++++++++ .../gameServer/messages/YachtEventType.java | 5 +- src/main/java/seng302/model/ClientYacht.java | 24 +++++++ src/main/java/seng302/model/token/Token.java | 7 ++ .../java/seng302/visualiser/GameClient.java | 9 ++- .../controllers/RaceViewController.java | 27 ++++---- 7 files changed, 147 insertions(+), 30 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index cfba8af1..496c7cb9 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -1,5 +1,6 @@ package seng302.gameServer; +import com.sun.corba.se.spi.activation.Server; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,6 +60,7 @@ public class GameState implements Runnable { //Powerup Constants private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; private static final Integer HANDLING_BOOST_MULTIPLIER = 2; + private static final Long BUMPER_DISABLE_TIME = 5_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -279,7 +281,7 @@ public class GameState implements Runnable { spawnNewToken(); notifyMessageListeners(MessageFactory.getRaceXML()); } - }, 0, 60000); + }, 0, 15_000); } // 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 * Broadasts a new race status message to show this update */ - public static void spawnNewToken() { + private void spawnNewToken() { Random random = new Random(); tokensInPlay.clear(); //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); - token.assignRandomType(); +// token.assignRandomType(); + token.assignType(TokenType.BUMPER); logger.debug("Spawned token of type " + token.getTokenType()); @@ -383,8 +386,9 @@ public class GameState implements Runnable { updateVelocity(yacht); yacht.runAutoPilot(); yacht.updateLocation(timeInterval); + preformTokenUpdates( + yacht); //This update must be done before collision. Sorry sorta hacky rn. checkCollision(yacht); - preformTokenUpdates(yacht); //This update must always be done lsat if (yacht.getBoatStatus() != BoatStatus.FINISHED) { checkForLegProgression(yacht); raceFinished = false; @@ -405,38 +409,66 @@ public class GameState implements Runnable { */ private void preformTokenUpdates(ServerYacht yacht) { checkTokenPickUp(yacht); + checkPowerUpTimeout(yacht); + TokenType powerUp = yacht.getPowerUp(); - if (yacht.getPowerUp() != null) { - switch (yacht.getPowerUp()) { + if (powerUp != null) { + switch (powerUp) { case WIND_WALKER: windWalk(yacht); break; case BUMPER: + ServerYacht collidedYacht = checkYachtCollision(yacht); + if (collidedYacht != null) { + yacht.powerDown(); + boatTempShutDown(collidedYacht); + notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht)); + notifyMessageListeners( + MessageFactory.makeStatusEffectMessage(yacht, powerUp)); + } 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 - * yacht down. WARNING. Do not call if the yacht does not have an active power up. Check with - * yacht.getPowerup != null first + * yacht down. * * @param yacht The yacht to check to power down */ private void checkPowerUpTimeout(ServerYacht yacht) { - if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() - .getTimeout()) { - yacht.powerDown(); - String logMessage = yacht.getBoatName() + "'s power-up token expired"; - notifyMessageListeners( - MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); - logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); + if (yacht.getPowerUp() != null) { + if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() + .getTimeout()) { + String logMessage = + yacht.getBoatName() + "'s " + yacht.getPowerUp().getName() + " expired"; + notifyMessageListeners( + MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); + notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht)); + logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); + + yacht.powerDown(); + } } } diff --git a/src/main/java/seng302/gameServer/MessageFactory.java b/src/main/java/seng302/gameServer/MessageFactory.java index d883d06c..2bb0d474 100644 --- a/src/main/java/seng302/gameServer/MessageFactory.java +++ b/src/main/java/seng302/gameServer/MessageFactory.java @@ -137,6 +137,14 @@ public class MessageFactory { 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) { YachtEventType yachtEventType = null; switch (token.getTokenType()) { @@ -159,6 +167,37 @@ public class MessageFactory { 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) { return new ChatterMessage(messageType, "SERVER: " + message); } diff --git a/src/main/java/seng302/gameServer/messages/YachtEventType.java b/src/main/java/seng302/gameServer/messages/YachtEventType.java index c8a997a0..facda84e 100644 --- a/src/main/java/seng302/gameServer/messages/YachtEventType.java +++ b/src/main/java/seng302/gameServer/messages/YachtEventType.java @@ -9,7 +9,10 @@ public enum YachtEventType { TOKEN_BUMPER(35), TOKEN_HANDLING(36), TOKEN_WIND_WALKER(37), - TOKEN_RANDOM(38); + TOKEN_RANDOM(38), + POWER_DOWN(39), + BUMPER_CRASH(40); + private int code; diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index b926f2ff..f626db30 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -39,11 +39,22 @@ public class ClientYacht extends Observable { void notifyRounding(ClientYacht yacht, int legNumber); } + //This notifies RaceViewController so it can display icon - wmu16 @FunctionalInterface public interface PowerUpListener { + 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); @@ -70,6 +81,8 @@ public class ClientYacht extends Observable { private List locationListeners = new ArrayList<>(); private List markRoundingListeners = new ArrayList<>(); private List powerUpListeners = new ArrayList<>(); + private List powerDownListeners = new ArrayList<>(); + private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); @@ -211,6 +224,13 @@ public class ClientYacht extends Observable { this.position = position; } + public void powerDown() { + this.powerUp = null; + for (PowerDownListener listener : powerDownListeners) { + listener.notifyPowerDown(this); + } + } + public void setPowerUp(TokenType tokenType) { this.powerUp = tokenType; for (PowerUpListener listener : powerUpListeners) { @@ -295,6 +315,10 @@ public class ClientYacht extends Observable { powerUpListeners.add(listener); } + public void addPowerDownListener(PowerDownListener listener) { + powerDownListeners.add(listener); + } + public void removeMarkRoundingListener(MarkRoundingListener listener) { markRoundingListeners.remove(listener); } diff --git a/src/main/java/seng302/model/token/Token.java b/src/main/java/seng302/model/token/Token.java index e1a16bd3..ed8b87eb 100644 --- a/src/main/java/seng302/model/token/Token.java +++ b/src/main/java/seng302/model/token/Token.java @@ -39,4 +39,11 @@ public class Token extends GeoPoint { tokenTypeList.remove(TokenType.RANDOM); tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size())); } + + /** + * Exists for testing purposes only + */ + public void assignType(TokenType tokenType) { + this.tokenType = tokenType; + } } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 9abf282a..9947719e 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -410,8 +410,15 @@ public class GameClient { * @param yachtEventData The YachtEvent data packet */ private void processYachtEvent(YachtEventData yachtEventData) { + ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue()); + if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { 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 { TokenType tokenType = null; if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { @@ -427,7 +434,7 @@ public class GameClient { } showTokenPickUp(tokenType); - allBoatsMap.get(yachtEventData.getSubjectId().intValue()).setPowerUp(tokenType); + thisYacht.setPowerUp(tokenType); } } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index e45dd091..1819ff48 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -48,6 +48,7 @@ import javafx.scene.shape.Polyline; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.stage.StageStyle; +import javax.swing.ImageIcon; import seng302.model.ClientYacht; import seng302.model.ClientYacht.PowerUpListener; import seng302.model.RaceState; @@ -140,6 +141,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private JFXDialog finishScreenDialog; private FinishDialogController finishDialogController; + //Icon stuff + private Timer blinkingTimer = new Timer(); + private ImageView iconToDisplay; + public void initialize() { Sounds.stopMusic(); Sounds.playRaceMusic(); @@ -253,6 +258,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel }); player.addPowerUpListener(this::displayPowerUpIcon); + player.addPowerDownListener(this::removeIcon); updateOrder(raceState.getPlayerPositions()); gameView = new GameView3D(); @@ -301,7 +307,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel */ private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) { if (yacht == player) { - final ImageView iconToDisplay; switch (tokenType) { case BOOST: @@ -324,7 +329,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel iconToDisplay.setVisible(true); //Start blinking icon towards end - Timer blinkingTimer = new Timer(); + if (blinkingTimer != null) { + blinkingTimer.cancel(); + } + blinkingTimer = new Timer("Blinking Timer"); blinkingTimer.schedule(new TimerTask() { Boolean isVisible = true; @@ -334,16 +342,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel iconToDisplay.setVisible(isVisible); } }, (int) (tokenType.getTimeout() * ICON_BLINK_TIMEOUT_RATIO), ICON_BLINK_PERIOD); + } + } - //Turn icon off after the time out - Timer switchOffTimer = new Timer(); - switchOffTimer.schedule(new TimerTask() { - @Override - public void run() { - blinkingTimer.cancel(); - iconToDisplay.setVisible(false); - } - }, tokenType.getTimeout()); + public void removeIcon(ClientYacht yacht) { + if (yacht == player) { + blinkingTimer.cancel(); + iconToDisplay.setVisible(false); } }