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;
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();
}
}
}
@@ -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);
}
@@ -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;
@@ -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<YachtLocationListener> locationListeners = new ArrayList<>();
private List<MarkRoundingListener> markRoundingListeners = new ArrayList<>();
private List<PowerUpListener> powerUpListeners = new ArrayList<>();
private List<PowerDownListener> 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);
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}