From 78596ea111c5ed027eb232bffb9e6808400786f2 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 19 Sep 2017 17:47:05 +1200 Subject: [PATCH 01/32] Initial commit for Power Up story Made new preliminary models for each power up. Currently just different colour balls Added new YachtEventTypes in the enum for each pick up to be sent out to clients Tokens now not only randomise location but also randomise which type of token will be sent out Added new methods to the MessageFactory class - Make collision and Make pickup Message Game Client now checks what type of Yacht Event code has come in to respond appropriately rather than just generic collision / token.. although this has not been implemented yet Game View loads appropriate token models depending on what is in XML #story[1245] --- .../java/seng302/gameServer/GameState.java | 27 +- .../seng302/gameServer/MessageFactory.java | 29 ++ .../gameServer/messages/YachtEventType.java | 6 +- src/main/java/seng302/model/token/Token.java | 4 + .../java/seng302/model/token/TokenType.java | 15 +- .../java/seng302/visualiser/GameClient.java | 31 +- .../java/seng302/visualiser/GameView3D.java | 23 +- .../fxObjects/assets_3D/ModelFactory.java | 22 +- .../fxObjects/assets_3D/ModelType.java | 4 + src/main/resources/meshes/bumper_pickup.dae | 147 ++++++++ src/main/resources/meshes/random_pickup.dae | 332 ++++++++++++++++++ src/main/resources/meshes/turning_pickup.dae | 147 ++++++++ .../resources/meshes/wind_walker_pickup.dae | 147 ++++++++ src/main/resources/views/RaceView.fxml | 2 +- 14 files changed, 900 insertions(+), 36 deletions(-) create mode 100644 src/main/resources/meshes/bumper_pickup.dae create mode 100644 src/main/resources/meshes/random_pickup.dae create mode 100644 src/main/resources/meshes/turning_pickup.dae create mode 100644 src/main/resources/meshes/wind_walker_pickup.dae diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 0b495a8e..c5317fa6 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -281,7 +281,13 @@ public class GameState implements Runnable { public static void spawnNewToken() { Random random = new Random(); tokensInPlay.clear(); - tokensInPlay.add(allTokens.get(random.nextInt(allTokens.size()))); + + //Get a random token location + Token token = allTokens.get(random.nextInt(allTokens.size())); + //Set a random type to this token + token.setTokenType(TokenType.values()[random.nextInt(TokenType.values().length)]); + + tokensInPlay.add(token); } /** @@ -397,9 +403,7 @@ public class GameState implements Runnable { collidedYacht.setCurrentVelocity( collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY ); - notifyMessageListeners( - new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION) - ); + notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht)); } //Mark Collision @@ -411,9 +415,7 @@ public class GameState implements Runnable { serverYacht.setCurrentVelocity( serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY ); - notifyMessageListeners( - new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION) - ); + notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht)); } //Boundary Collision @@ -426,22 +428,21 @@ public class GameState implements Runnable { serverYacht.setCurrentVelocity( serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY ); - notifyMessageListeners( - new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION) - ); + notifyMessageListeners(MessageFactory.makeCollisionMessage(serverYacht)); } //Token Collision Token collidedToken = checkTokenPickUp(serverYacht); if (collidedToken != null) { - sendServerMessage(serverYacht.getSourceId(), serverYacht.getBoatName() + " has picked speed-up token"); + 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( - new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.TOKEN)); + notifyMessageListeners(MessageFactory.makePickupMessage(serverYacht, collidedToken)); } } diff --git a/src/main/java/seng302/gameServer/MessageFactory.java b/src/main/java/seng302/gameServer/MessageFactory.java index a71733cf..2ef38d29 100644 --- a/src/main/java/seng302/gameServer/MessageFactory.java +++ b/src/main/java/seng302/gameServer/MessageFactory.java @@ -11,11 +11,14 @@ import seng302.gameServer.messages.RaceStatusMessage; import seng302.gameServer.messages.RaceType; import seng302.gameServer.messages.XMLMessage; import seng302.gameServer.messages.XMLMessageSubType; +import seng302.gameServer.messages.YachtEventCodeMessage; +import seng302.gameServer.messages.YachtEventType; import seng302.model.Player; import seng302.model.ServerYacht; import seng302.model.stream.xml.generator.RaceXMLTemplate; import seng302.model.stream.xml.generator.RegattaXMLTemplate; import seng302.model.token.Token; +import seng302.model.token.TokenType; import seng302.utilities.XMLGenerator; /** @@ -128,4 +131,30 @@ public class MessageFactory { XMLMessageSubType.BOAT, xmlGenerator.getBoatsAsXml().length()); } + + public static YachtEventCodeMessage makeCollisionMessage(ServerYacht serverYacht) { + return new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION); + } + + public static YachtEventCodeMessage makePickupMessage(ServerYacht serverYacht, Token token) { + YachtEventType yachtEventType = null; + switch (token.getTokenType()) { + case BOOST: + yachtEventType = YachtEventType.TOKEN_VELOCITY; + break; + case HANDLING: + yachtEventType = YachtEventType.TOKEN_HANDLING; + break; + case WIND_WALKER: + yachtEventType = YachtEventType.TOKEN_WIND_WALKER; + break; + case BUMPER: + yachtEventType = YachtEventType.TOKEN_BUMPER; + break; + case RANDOM: + yachtEventType = YachtEventType.TOKEN_RANDOM; + break; + } + return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType); + } } diff --git a/src/main/java/seng302/gameServer/messages/YachtEventType.java b/src/main/java/seng302/gameServer/messages/YachtEventType.java index b31d45a1..c8a997a0 100644 --- a/src/main/java/seng302/gameServer/messages/YachtEventType.java +++ b/src/main/java/seng302/gameServer/messages/YachtEventType.java @@ -5,7 +5,11 @@ package seng302.gameServer.messages; */ public enum YachtEventType { COLLISION(33), - TOKEN(34); + TOKEN_VELOCITY(34), + TOKEN_BUMPER(35), + TOKEN_HANDLING(36), + TOKEN_WIND_WALKER(37), + TOKEN_RANDOM(38); private int code; diff --git a/src/main/java/seng302/model/token/Token.java b/src/main/java/seng302/model/token/Token.java index 9f8ca619..56536da3 100644 --- a/src/main/java/seng302/model/token/Token.java +++ b/src/main/java/seng302/model/token/Token.java @@ -18,4 +18,8 @@ public class Token extends GeoPoint { public TokenType getTokenType() { return tokenType; } + + public void setTokenType(TokenType tokenType) { + this.tokenType = tokenType; + } } diff --git a/src/main/java/seng302/model/token/TokenType.java b/src/main/java/seng302/model/token/TokenType.java index d5c2df01..e85c6bec 100644 --- a/src/main/java/seng302/model/token/TokenType.java +++ b/src/main/java/seng302/model/token/TokenType.java @@ -5,19 +5,28 @@ package seng302.model.token; * Created by wmu16 on 28/08/17. */ public enum TokenType { - BOOST(0), - HANDLING(1); + BOOST(0, "Boost"), + HANDLING(1, "Handling"), + BUMPER(2, "Bumper"), + RANDOM(3, "Random"), + WIND_WALKER(4, "Wind Walker"); private int value; + private String name; - TokenType(int value) { + TokenType(int value, String name) { this.value = value; + this.name = name; } public int getValue() { return value; } + public String getName() { + return name; + } + public static TokenType getToken(int value) { switch (value) { case 0: diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 7040d7ee..5cab5040 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -245,12 +245,7 @@ public class GameClient { break; case YACHT_EVENT_CODE: - YachtEventData yachtEventData = StreamParser.extractYachtEventCode(packet); - if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { - showCollisionAlert(StreamParser.extractYachtEventCode(packet)); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN.getCode()) { - showPickUp(); - } + displayYachtEvent(StreamParser.extractYachtEventCode(packet)); break; case CHATTER_TEXT: @@ -407,6 +402,28 @@ public class GameClient { return courseData; } + + /** + * Appropriately displays the event client side given the YachtEventCode (collision / token..) + * + * @param yachtEventData The YachtEvent data packet + */ + private void displayYachtEvent(YachtEventData yachtEventData) { + if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { + showCollisionAlert(yachtEventData); + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { + showPickUp(yachtEventData); + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_BUMPER.getCode()) { + showPickUp(yachtEventData); + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_HANDLING.getCode()) { + showPickUp(yachtEventData); + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_RANDOM.getCode()) { + showPickUp(yachtEventData); + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_WIND_WALKER.getCode()) { + showPickUp(yachtEventData); + } + } + /** * Tells race view to show a collision animation. */ @@ -420,7 +437,7 @@ public class GameClient { } // TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user - private void showPickUp() { + private void showPickUp(YachtEventData yachtEventData) { Sounds.playTokenPickupSound(); } diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index c5b52286..6af253c9 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -27,6 +27,7 @@ import seng302.model.mark.CompoundMark; import seng302.model.mark.Corner; import seng302.model.mark.Mark; import seng302.model.token.Token; +import seng302.model.token.TokenType; import seng302.utilities.GeoUtility; import seng302.utilities.Sounds; import seng302.visualiser.fxObjects.MarkArrowFactory; @@ -556,7 +557,27 @@ public class GameView3D { mapTokens = new ArrayList<>(); for (Token token : newTokens) { Point2D location = findScaledXY(token.getLat(), token.getLng()); - Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets(); + + ModelType modelType = null; + switch (token.getTokenType()) { + case BOOST: + modelType = ModelType.VELOCITY_PICKUP; + break; + case HANDLING: + modelType = ModelType.HANDLING_PICKUP; + break; + case BUMPER: + modelType = ModelType.BUMPER_PICKUP; + break; + case RANDOM: + modelType = ModelType.RANDOM_PICKUP; + break; + case WIND_WALKER: + modelType = ModelType.WIND_WALKER_PICKUP; + break; + } + + Node tokenObject = ModelFactory.importModel(modelType).getAssets(); tokenObject.setLayoutX(location.getX()); tokenObject.setLayoutY(location.getY()); mapTokens.add(tokenObject); diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelFactory.java b/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelFactory.java index f0c50abe..8d7ecadf 100644 --- a/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelFactory.java +++ b/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelFactory.java @@ -118,7 +118,11 @@ public class ModelFactory { } switch (tokenType) { case VELOCITY_PICKUP: - return makeCoinPickup(assets); + case BUMPER_PICKUP: + case RANDOM_PICKUP: + case HANDLING_PICKUP: + case WIND_WALKER_PICKUP: + return makeTokenPickup(assets); case FINISH_MARKER: case PLAIN_MARKER: case START_MARKER: @@ -147,24 +151,22 @@ public class ModelFactory { } } - private static Model makeCoinPickup(Group assets){ - assets.setRotationAxis(new Point3D(1,0,0)); - assets.setRotate(90); - assets.setTranslateX(0.2); - assets.setTranslateY(1); + private static Model makeTokenPickup(Group assets) { + Rotate animationRotate = new Rotate(0, new Point3D(0, 0, 1)); assets.getTransforms().addAll( - new Translate(0,-1,0), - new Rotate(0 ,new Point3D(1,1,1)) + animationRotate, + new Translate(0, 0, -1) ); + return new Model(new Group(assets), new AnimationTimer() { private double rotation = 0; - private Group group = assets; + private Rotate rotate = animationRotate; @Override public void handle(long now) { rotation += 1; - ((Rotate) group.getTransforms().get(1)).setAngle(rotation); + rotate.setAngle(rotation); } }); } diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelType.java b/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelType.java index 1c31a553..06df2eda 100644 --- a/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelType.java +++ b/src/main/java/seng302/visualiser/fxObjects/assets_3D/ModelType.java @@ -7,6 +7,10 @@ package seng302.visualiser.fxObjects.assets_3D; public enum ModelType { VELOCITY_PICKUP("velocity_pickup.dae"), + HANDLING_PICKUP("turning_pickup.dae"), + WIND_WALKER_PICKUP("wind_walker_pickup.dae"), + BUMPER_PICKUP("bumper_pickup.dae"), + RANDOM_PICKUP("random_pickup.dae"), FINISH_MARKER ("finish_marker.dae"), START_MARKER ("start_marker.dae"), PLAIN_MARKER ("plain_marker.dae"), diff --git a/src/main/resources/meshes/bumper_pickup.dae b/src/main/resources/meshes/bumper_pickup.dae new file mode 100644 index 00000000..efc8503f --- /dev/null +++ b/src/main/resources/meshes/bumper_pickup.dae @@ -0,0 +1,147 @@ + + + + + Blender User + Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22 + + 2017-09-19T15:47:50 + 2017-09-19T15:47:50 + + Z_UP + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.64 6.87021e-4 0 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + + + + + + + + + 0 0 -1 0.7236073 -0.5257253 -0.4472195 -0.276388 -0.8506492 -0.4472199 -0.8944262 0 -0.4472156 -0.276388 0.8506492 -0.4472199 0.7236073 0.5257253 -0.4472195 0.276388 -0.8506492 0.4472199 -0.7236073 -0.5257253 0.4472195 -0.7236073 0.5257253 0.4472195 0.276388 0.8506492 0.4472199 0.8944262 0 0.4472156 0 0 1 -0.1624554 -0.4999952 -0.8506544 0.4253227 -0.3090114 -0.8506542 0.2628688 -0.8090116 -0.5257377 0.8506479 0 -0.5257359 0.4253227 0.3090114 -0.8506542 -0.5257298 0 -0.8506517 -0.6881894 -0.4999969 -0.5257362 -0.1624554 0.4999952 -0.8506544 -0.6881894 0.4999969 -0.5257362 0.2628688 0.8090116 -0.5257377 0.9510579 -0.3090126 0 0.9510579 0.3090126 0 0 -1 0 0.5877856 -0.8090167 0 -0.9510579 -0.3090126 0 -0.5877856 -0.8090167 0 -0.5877856 0.8090167 0 -0.9510579 0.3090126 0 0.5877856 0.8090167 0 0 1 0 0.6881894 -0.4999969 0.5257362 -0.2628688 -0.8090116 0.5257377 -0.8506479 0 0.5257359 -0.2628688 0.8090116 0.5257377 0.6881894 0.4999969 0.5257362 0.1624554 -0.4999952 0.8506544 0.5257298 0 0.8506517 -0.4253227 -0.3090114 0.8506542 -0.4253227 0.3090114 0.8506542 0.1624554 0.4999952 0.8506544 + + + + + + + + + + 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616991 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.1875942 0.5773453 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 + + + + + + + + + + 0.1891562 0.4353454 0.2201194 0.3408505 0.2923961 0.4111046 0.1891562 0.4353454 0.2923961 0.4111046 0.2610388 0.5272238 0.06585121 0.2636542 0.1202549 0.3546833 1.02655e-4 0.3599037 0.1891674 0.09197556 0.120263 0.1726311 0.07693755 0.06042814 0.3900915 0.1577098 0.2924051 0.1162222 0.3863589 0.0410583 0.3900838 0.3696232 0.3989981 0.2636671 0.4998953 0.3296785 0.6099237 0.1577003 0.7076132 0.1162198 0.6911104 0.216345 0.6099155 0.3696137 0.6010091 0.2636568 0.6911069 0.3109756 0.8108381 0.4353509 0.7076001 0.4111025 0.779882 0.3408538 0.934156 0.263669 0.8797454 0.354694 0.833759 0.263666 0.8108526 0.09198099 0.8797511 0.1726418 0.7798885 0.1864736 0.7798885 0.1864736 0.8797511 0.1726418 0.833759 0.263666 0.8797511 0.1726418 0.934156 0.263669 0.833759 0.263666 0.833759 0.263666 0.8797454 0.354694 0.779882 0.3408538 0.8797454 0.354694 0.8108381 0.4353509 0.779882 0.3408538 0.779882 0.3408538 0.7076001 0.4111025 0.6911069 0.3109756 0.7076001 0.4111025 0.6099155 0.3696137 0.6911069 0.3109756 0.6911069 0.3109756 0.6010091 0.2636568 0.6911104 0.216345 0.6010091 0.2636568 0.6099237 0.1577003 0.6911104 0.216345 0.6911104 0.216345 0.7076132 0.1162198 0.7798885 0.1864736 0.7076132 0.1162198 0.8108526 0.09198099 0.7798885 0.1864736 0.923085 0.06044203 0.8797511 0.1726418 0.8108526 0.09198099 0.923085 0.06044203 0.9998974 0.16742 0.8797511 0.1726418 0.9998974 0.16742 0.934156 0.263669 0.8797511 0.1726418 0.9998974 0.3599234 0.8797454 0.354694 0.934156 0.263669 0.9998974 0.3599234 0.923074 0.4668999 0.8797454 0.354694 0.923074 0.4668999 0.8108381 0.4353509 0.8797454 0.354694 0.7389487 0.527224 0.7076001 0.4111025 0.8108381 0.4353509 0.7389487 0.527224 0.6136447 0.4862654 0.7076001 0.4111025 0.6136447 0.4862654 0.6099155 0.3696137 0.7076001 0.4111025 0.5001069 0.3296607 0.6010091 0.2636568 0.6099155 0.3696137 0.5001069 0.3296607 0.5001106 0.1976431 0.6010091 0.2636568 0.5001106 0.1976431 0.6099237 0.1577003 0.6010091 0.2636568 0.613665 0.041049 0.7076132 0.1162198 0.6099237 0.1577003 0.613665 0.041049 0.7389741 1.02655e-4 0.7076132 0.1162198 0.7389741 1.02655e-4 0.8108526 0.09198099 0.7076132 0.1162198 0.4998953 0.3296785 0.3989981 0.2636671 0.4999017 0.1976609 0.3989981 0.2636671 0.3900915 0.1577098 0.4999017 0.1976609 0.3863589 0.0410583 0.2924051 0.1162222 0.2610529 1.02655e-4 0.2924051 0.1162222 0.1891674 0.09197556 0.2610529 1.02655e-4 0.07693755 0.06042814 0.120263 0.1726311 1.17172e-4 0.1674003 0.120263 0.1726311 0.06585121 0.2636542 1.17172e-4 0.1674003 1.02655e-4 0.3599037 0.1202549 0.3546833 0.07691794 0.466886 0.1202549 0.3546833 0.1891562 0.4353454 0.07691794 0.466886 0.2610388 0.5272238 0.2923961 0.4111046 0.3863458 0.4862747 0.2923961 0.4111046 0.3900838 0.3696232 0.3863458 0.4862747 0.3088967 0.3109791 0.3989981 0.2636671 0.3900838 0.3696232 0.3088967 0.3109791 0.3089004 0.2163485 0.3989981 0.2636671 0.3089004 0.2163485 0.3900915 0.1577098 0.3989981 0.2636671 0.3089004 0.2163485 0.2924051 0.1162222 0.3900915 0.1577098 0.3089004 0.2163485 0.2201245 0.1864705 0.2924051 0.1162222 0.2201245 0.1864705 0.1891674 0.09197556 0.2924051 0.1162222 0.2201245 0.1864705 0.120263 0.1726311 0.1891674 0.09197556 0.2201245 0.1864705 0.1662483 0.2636588 0.120263 0.1726311 0.1662483 0.2636588 0.06585121 0.2636542 0.120263 0.1726311 0.2923961 0.4111046 0.3088967 0.3109791 0.3900838 0.3696232 0.2923961 0.4111046 0.2201194 0.3408505 0.3088967 0.3109791 0.1662483 0.2636588 0.1202549 0.3546833 0.06585121 0.2636542 0.1662483 0.2636588 0.2201194 0.3408505 0.1202549 0.3546833 0.2201194 0.3408505 0.1891562 0.4353454 0.1202549 0.3546833 0.2038319 0.6020938 0.07803589 0.775239 2.88122e-4 0.5359594 0.2038319 0.6020938 2.88122e-4 0.5359594 0.2038319 0.3880734 0.2038319 0.6020938 0.2038319 0.3880734 0.4073758 0.5359594 0.2038319 0.6020938 0.4073758 0.5359594 0.329628 0.775239 0.8690316 0.364753 0.791095 0.5663278 0.6529134 0.364753 0.5301482 0.1791203 0.6523408 0.3573763 0.407952 0.3573763 0.5301446 0.5663277 0.407952 0.3880734 0.6523372 0.3880734 0.20244 0.9919336 0.20244 0.7758153 0.4040137 0.9139961 0.4079523 0.566904 0.6095281 0.6448412 0.407952 0.7830209 0.203883 2.88122e-4 0.4073758 0.1482443 0.2038091 0.2143085 0.4073758 0.1482443 0.3295453 0.3874972 0.2038091 0.2143085 0.3295453 0.3874972 0.07795333 0.3874103 0.2038091 0.2143085 0.07795333 0.3874103 2.88122e-4 0.1481037 0.2038091 0.2143085 2.88122e-4 0.1481037 0.203883 2.88122e-4 0.2038091 0.2143085 0.6523409 0.178544 0.407952 0.1785441 0.5301446 2.88122e-4 0.7910969 0.2018641 0.6529171 2.88122e-4 0.8690339 2.88122e-4 0.8116793 0.6448401 0.6101047 0.7830221 0.6101044 0.566904 2.88169e-4 0.7758153 0.2018638 0.9139932 2.88122e-4 0.9919315 0.407952 0.7835971 0.6095241 0.9217739 0.407952 0.999712 0.07803589 0.775239 0.2038319 0.6020938 0.329628 0.775239 + + + + + + + + + + + + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

1 0 0 13 0 1 15 0 2 1 1 3 15 1 4 22 1 5 2 2 6 14 2 7 24 2 8 3 3 9 18 3 10 26 3 11 4 4 12 20 4 13 28 4 14 5 5 15 21 5 16 30 5 17 6 6 18 32 6 19 37 6 20 7 7 21 33 7 22 39 7 23 8 8 24 34 8 25 40 8 26 9 9 27 35 9 28 41 9 29 10 10 30 36 10 31 38 10 32 38 11 33 36 11 34 41 11 35 36 12 36 9 12 37 41 12 38 41 13 39 35 13 40 40 13 41 35 14 42 8 14 43 40 14 44 40 15 45 34 15 46 39 15 47 34 16 48 7 16 49 39 16 50 39 17 51 33 17 52 37 17 53 33 18 54 6 18 55 37 18 56 37 19 57 32 19 58 38 19 59 32 20 60 10 20 61 38 20 62 23 21 63 36 21 64 10 21 65 23 22 66 30 22 67 36 22 68 30 23 69 9 23 70 36 23 71 31 24 72 35 24 73 9 24 74 31 25 75 28 25 76 35 25 77 28 26 78 8 26 79 35 26 80 29 27 81 34 27 82 8 27 83 29 28 84 26 28 85 34 28 86 26 29 87 7 29 88 34 29 89 27 30 90 33 30 91 7 30 92 27 31 93 24 31 94 33 31 95 24 32 96 6 32 97 33 32 98 25 33 99 32 33 100 6 33 101 25 34 102 22 34 103 32 34 104 22 35 105 10 35 106 32 35 107 30 36 108 21 36 109 31 36 110 21 37 111 4 37 112 31 37 113 28 38 114 20 38 115 29 38 116 20 39 117 3 39 118 29 39 119 26 40 120 18 40 121 27 40 122 18 41 123 2 41 124 27 41 125 24 42 126 14 42 127 25 42 128 14 43 129 1 43 130 25 43 131 22 44 132 15 44 133 23 44 134 15 45 135 5 45 136 23 45 137 16 46 138 21 46 139 5 46 140 16 47 141 19 47 142 21 47 143 19 48 144 4 48 145 21 48 146 19 49 147 20 49 148 4 49 149 19 50 150 17 50 151 20 50 152 17 51 153 3 51 154 20 51 155 17 52 156 18 52 157 3 52 158 17 53 159 12 53 160 18 53 161 12 54 162 2 54 163 18 54 164 15 55 165 16 55 166 5 55 167 15 56 168 13 56 169 16 56 170 12 57 171 14 57 172 2 57 173 12 58 174 13 58 175 14 58 176 13 59 177 1 59 178 14 59 179

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

0 60 180 13 60 181 12 60 182 0 61 183 12 61 184 17 61 185 0 62 186 17 62 187 19 62 188 0 63 189 19 63 190 16 63 191 1 64 192 22 64 193 25 64 194 2 65 195 24 65 196 27 65 197 3 66 198 26 66 199 29 66 200 4 67 201 28 67 202 31 67 203 5 68 204 30 68 205 23 68 206 38 69 207 41 69 208 11 69 209 41 70 210 40 70 211 11 70 212 40 71 213 39 71 214 11 71 215 39 72 216 37 72 217 11 72 218 37 73 219 38 73 220 11 73 221 30 74 222 31 74 223 9 74 224 28 75 225 29 75 226 8 75 227 26 76 228 27 76 229 7 76 230 24 77 231 25 77 232 6 77 233 22 78 234 23 78 235 10 78 236 13 79 237 0 79 238 16 79 239

+
+
+
+
+ + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/main/resources/meshes/random_pickup.dae b/src/main/resources/meshes/random_pickup.dae new file mode 100644 index 00000000..56d830fc --- /dev/null +++ b/src/main/resources/meshes/random_pickup.dae @@ -0,0 +1,332 @@ + + + + + Blender User + Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22 + + 2017-09-19T15:54:38 + 2017-09-19T15:54:38 + + Z_UP + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.8 0.6035923 0 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.8 0.1522076 0 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.01023849 0.004662884 0.64 1 + + + 0.5 0.5 0.5 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.64 0 0.2456551 1 + + + 0.5 0.5 0.5 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 8.07677e-5 0.64 0 1 + + + 0.5 0.5 0.5 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.64 0 0.005309466 1 + + + 0.5 0.5 0.5 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.5 0.5 0.5 1 + + + 50 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 0 -1 0.7236073 -0.5257253 -0.4472195 -0.276388 -0.8506492 -0.4472199 -0.8944262 0 -0.4472156 -0.276388 0.8506492 -0.4472199 0.7236073 0.5257253 -0.4472195 0.276388 -0.8506492 0.4472199 -0.7236073 -0.5257253 0.4472195 -0.7236073 0.5350354 0.4472195 0.276388 0.8506492 0.4472199 0.8944262 0 0.4472156 0 0 1 -0.1624554 -0.4999952 -0.8506544 0.4253227 -0.3090114 -0.8506542 0.2628688 -0.8090116 -0.5257377 0.8506479 0 -0.5257359 0.4253227 0.3090114 -0.8506542 -0.5257298 0 -0.8506517 -0.6881894 -0.4999969 -0.5257362 -0.1624554 0.4999952 -0.8506544 -0.6881894 0.4999969 -0.5257362 0.2628688 0.8090116 -0.5257377 0.9510579 -0.3090126 0 0.9510579 0.3090126 0 0 -1 0 0.5877856 -0.8090167 0 -0.9510579 -0.3090126 0 -0.5877856 -0.8090167 0 -0.5877856 0.8183268 0 -0.9510579 0.3090126 0 0.5877856 0.8090167 0 0 1 0 0.6881894 -0.4999969 0.5257362 -0.2628688 -0.8090116 0.5257377 -0.8506479 0 0.5257359 -0.2628688 0.8183217 0.5257377 0.6881894 0.4999969 0.5257362 0.1624554 -0.4999952 0.8506544 0.5257298 0 0.8506517 -0.4253227 -0.3090114 0.8506542 -0.4253227 0.3090114 0.8506542 0.1624554 0.4999952 0.8506544 + + + + + + + + + + 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.570192 0.7464457 -0.3430743 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.6991799 0.2635454 0.6645987 0.05184978 0.746441 0.6634285 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.185151 0.569826 0.8006358 -0.4685443 0.5765037 0.6694099 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.009833872 0.9466192 0.3222044 -0.2904988 0.9398801 0.1795436 -0.5345759 0.7778646 0.3303867 -0.9071158 0.2635452 0.3281539 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7989099 0.5698285 -0.1924537 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.2925817 0.9466192 -0.1353079 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.8082743 0.5765079 0.1197141 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 + + + + + + + + + + 0.1891562 0.4353454 0.2201194 0.3408505 0.2923961 0.4111046 0.1891562 0.4353454 0.2923961 0.4111046 0.2610388 0.5272238 0.06585121 0.2636542 0.1202549 0.3546833 1.02655e-4 0.3599037 0.1891674 0.09197556 0.120263 0.1726311 0.07693755 0.06042814 0.3900915 0.1577098 0.2924051 0.1162222 0.3863589 0.0410583 0.3900838 0.3696232 0.3989981 0.2636671 0.4998953 0.3296785 0.6099237 0.1577003 0.7076132 0.1162198 0.6911104 0.216345 0.6099155 0.3696137 0.6010091 0.2636568 0.6911069 0.3109756 0.8108381 0.4353509 0.7076001 0.4111025 0.779882 0.3408538 0.934156 0.263669 0.8797454 0.354694 0.833759 0.263666 0.8108526 0.09198099 0.8797511 0.1726418 0.7798885 0.1864736 0.7798885 0.1864736 0.8797511 0.1726418 0.833759 0.263666 0.8797511 0.1726418 0.934156 0.263669 0.833759 0.263666 0.833759 0.263666 0.8797454 0.354694 0.779882 0.3408538 0.8797454 0.354694 0.8108381 0.4353509 0.779882 0.3408538 0.779882 0.3408538 0.7076001 0.4111025 0.6911069 0.3109756 0.7076001 0.4111025 0.6099155 0.3696137 0.6911069 0.3109756 0.6911069 0.3109756 0.6010091 0.2636568 0.6911104 0.216345 0.6010091 0.2636568 0.6099237 0.1577003 0.6911104 0.216345 0.6911104 0.216345 0.7076132 0.1162198 0.7798885 0.1864736 0.7076132 0.1162198 0.8108526 0.09198099 0.7798885 0.1864736 0.923085 0.06044203 0.8797511 0.1726418 0.8108526 0.09198099 0.923085 0.06044203 0.9998974 0.16742 0.8797511 0.1726418 0.9998974 0.16742 0.934156 0.263669 0.8797511 0.1726418 0.9998974 0.3599234 0.8797454 0.354694 0.934156 0.263669 0.9998974 0.3599234 0.923074 0.4668999 0.8797454 0.354694 0.923074 0.4668999 0.8108381 0.4353509 0.8797454 0.354694 0.7389487 0.527224 0.7076001 0.4111025 0.8108381 0.4353509 0.7389487 0.527224 0.6136447 0.4862654 0.7076001 0.4111025 0.6136447 0.4862654 0.6099155 0.3696137 0.7076001 0.4111025 0.5001069 0.3296607 0.6010091 0.2636568 0.6099155 0.3696137 0.5001069 0.3296607 0.5001106 0.1976431 0.6010091 0.2636568 0.5001106 0.1976431 0.6099237 0.1577003 0.6010091 0.2636568 0.613665 0.041049 0.7076132 0.1162198 0.6099237 0.1577003 0.613665 0.041049 0.7389741 1.02655e-4 0.7076132 0.1162198 0.7389741 1.02655e-4 0.8108526 0.09198099 0.7076132 0.1162198 0.4998953 0.3296785 0.3989981 0.2636671 0.4999017 0.1976609 0.3989981 0.2636671 0.3900915 0.1577098 0.4999017 0.1976609 0.3863589 0.0410583 0.2924051 0.1162222 0.2610529 1.02655e-4 0.2924051 0.1162222 0.1891674 0.09197556 0.2610529 1.02655e-4 0.07693755 0.06042814 0.120263 0.1726311 1.17172e-4 0.1674003 0.120263 0.1726311 0.06585121 0.2636542 1.17172e-4 0.1674003 1.02655e-4 0.3599037 0.1202549 0.3546833 0.07691794 0.466886 0.1202549 0.3546833 0.1891562 0.4353454 0.07691794 0.466886 0.2610388 0.5272238 0.2923961 0.4111046 0.3863458 0.4862747 0.2923961 0.4111046 0.3900838 0.3696232 0.3863458 0.4862747 0.3088967 0.3109791 0.3989981 0.2636671 0.3900838 0.3696232 0.3088967 0.3109791 0.3089004 0.2163485 0.3989981 0.2636671 0.3089004 0.2163485 0.3900915 0.1577098 0.3989981 0.2636671 0.3089004 0.2163485 0.2924051 0.1162222 0.3900915 0.1577098 0.3089004 0.2163485 0.2201245 0.1864705 0.2924051 0.1162222 0.2201245 0.1864705 0.1891674 0.09197556 0.2924051 0.1162222 0.2201245 0.1864705 0.120263 0.1726311 0.1891674 0.09197556 0.2201245 0.1864705 0.1662483 0.2636588 0.120263 0.1726311 0.1662483 0.2636588 0.06585121 0.2636542 0.120263 0.1726311 0.2923961 0.4111046 0.3088967 0.3109791 0.3900838 0.3696232 0.2923961 0.4111046 0.2201194 0.3408505 0.3088967 0.3109791 0.1662483 0.2636588 0.1202549 0.3546833 0.06585121 0.2636542 0.1662483 0.2636588 0.2201194 0.3408505 0.1202549 0.3546833 0.2201194 0.3408505 0.1891562 0.4353454 0.1202549 0.3546833 0.2038319 0.6020938 0.07803589 0.775239 2.88122e-4 0.5359594 0.2038319 0.6020938 2.88122e-4 0.5359594 0.2038319 0.3880734 0.2038319 0.6020938 0.2038319 0.3880734 0.4073758 0.5359594 0.2038319 0.6020938 0.4073758 0.5359594 0.329628 0.775239 0.8690316 0.364753 0.791095 0.5663278 0.6529134 0.364753 0.5301482 0.1791203 0.6523408 0.3573763 0.407952 0.3573763 0.5301446 0.5663277 0.407952 0.3880734 0.6523372 0.3880734 0.20244 0.9919336 0.20244 0.7758153 0.4040137 0.9139961 0.4079523 0.566904 0.6095281 0.6448412 0.407952 0.7830209 0.203883 2.88122e-4 0.4073758 0.1482443 0.2038091 0.2143085 0.4073758 0.1482443 0.3295453 0.3874972 0.2038091 0.2143085 0.3295453 0.3874972 0.07795333 0.3874103 0.2038091 0.2143085 0.07795333 0.3874103 2.88122e-4 0.1481037 0.2038091 0.2143085 2.88122e-4 0.1481037 0.203883 2.88122e-4 0.2038091 0.2143085 0.6523409 0.178544 0.407952 0.1785441 0.5301446 2.88122e-4 0.7910969 0.2018641 0.6529171 2.88122e-4 0.8690339 2.88122e-4 0.8116793 0.6448401 0.6101047 0.7830221 0.6101044 0.566904 2.88169e-4 0.7758153 0.2018638 0.9139932 2.88122e-4 0.9919315 0.407952 0.7835971 0.6095241 0.9217739 0.407952 0.999712 0.07803589 0.775239 0.2038319 0.6020938 0.329628 0.775239 + + + + + + + + + + + + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

36 12 36 9 12 37 41 12 38 35 14 42 8 14 43 40 14 44 34 16 48 7 16 49 39 16 50 33 18 54 6 18 55 37 18 56 32 20 60 10 20 61 38 20 62 30 23 69 9 23 70 36 23 71 28 26 78 8 26 79 35 26 80 26 29 87 7 29 88 34 29 89 24 32 96 6 32 97 33 32 98 22 35 105 10 35 106 32 35 107 16 47 141 19 47 142 21 47 143 19 50 150 17 50 151 20 50 152 17 53 159 12 53 160 18 53 161 15 56 168 13 56 169 16 56 170 12 58 174 13 58 175 14 58 176

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

0 60 180 13 60 181 12 60 182 0 61 183 12 61 184 17 61 185 0 62 186 17 62 187 19 62 188 0 63 189 19 63 190 16 63 191 1 64 192 22 64 193 25 64 194 2 65 195 24 65 196 27 65 197 3 66 198 26 66 199 29 66 200 4 67 201 28 67 202 31 67 203 5 68 204 30 68 205 23 68 206 38 69 207 41 69 208 11 69 209 41 70 210 40 70 211 11 70 212 40 71 213 39 71 214 11 71 215 39 72 216 37 72 217 11 72 218 37 73 219 38 73 220 11 73 221 30 74 222 31 74 223 9 74 224 28 75 225 29 75 226 8 75 227 26 76 228 27 76 229 7 76 230 24 77 231 25 77 232 6 77 233 22 78 234 23 78 235 10 78 236 13 79 237 0 79 238 16 79 239

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 +

23 22 66 30 22 67 36 22 68 31 25 75 28 25 76 35 25 77 29 28 84 26 28 85 34 28 86 27 31 93 24 31 94 33 31 95 25 34 102 22 34 103 32 34 104 30 36 108 21 36 109 31 36 110 28 38 114 20 38 115 29 38 116 26 40 120 18 40 121 27 40 122 24 42 126 14 42 127 25 42 128 22 44 132 15 44 133 23 44 134

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 +

6 6 18 32 6 19 37 6 20 7 7 21 33 7 22 39 7 23 8 8 24 34 8 25 40 8 26 9 9 27 35 9 28 41 9 29 10 10 30 36 10 31 38 10 32 23 21 63 36 21 64 10 21 65 31 24 72 35 24 73 9 24 74 29 27 81 34 27 82 8 27 83 27 30 90 33 30 91 7 30 92 25 33 99 32 33 100 6 33 101

+
+ + + + + 3 3 3 3 3 +

38 11 33 36 11 34 41 11 35 41 13 39 35 13 40 40 13 41 40 15 45 34 15 46 39 15 47 39 17 51 33 17 52 37 17 53 37 19 57 32 19 58 38 19 59

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 +

1 0 0 13 0 1 15 0 2 1 1 3 15 1 4 22 1 5 2 2 6 14 2 7 24 2 8 3 3 9 18 3 10 26 3 11 4 4 12 20 4 13 28 4 14 5 5 15 21 5 16 30 5 17 16 46 138 21 46 139 5 46 140 19 49 147 20 49 148 4 49 149 17 52 156 18 52 157 3 52 158 12 57 171 14 57 172 2 57 173

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 +

21 37 111 4 37 112 31 37 113 20 39 117 3 39 118 29 39 119 18 41 123 2 41 124 27 41 125 14 43 129 1 43 130 25 43 131 15 45 135 5 45 136 23 45 137 19 48 144 4 48 145 21 48 146 17 51 153 3 51 154 20 51 155 12 54 162 2 54 163 18 54 164 15 55 165 16 55 166 5 55 167 13 59 177 1 59 178 14 59 179

+
+
+
+
+ + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/main/resources/meshes/turning_pickup.dae b/src/main/resources/meshes/turning_pickup.dae new file mode 100644 index 00000000..2c4305f5 --- /dev/null +++ b/src/main/resources/meshes/turning_pickup.dae @@ -0,0 +1,147 @@ + + + + + Blender User + Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22 + + 2017-09-19T15:45:46 + 2017-09-19T15:45:46 + + Z_UP + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.004555753 0.0885511 0.003947978 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.64 0.1458963 0.001825521 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + + + + + + + + + 0 0 -1 0.7236073 -0.5257253 -0.4472195 -0.276388 -0.8506492 -0.4472199 -0.8944262 0 -0.4472156 -0.276388 0.8506492 -0.4472199 0.7236073 0.5257253 -0.4472195 0.276388 -0.8506492 0.4472199 -0.7236073 -0.5257253 0.4472195 -0.7236073 0.5257253 0.4472195 0.276388 0.8506492 0.4472199 0.8944262 0 0.4472156 0 0 1 -0.1624554 -0.4999952 -0.8506544 0.4253227 -0.3090114 -0.8506542 0.2628688 -0.8090116 -0.5257377 0.8506479 0 -0.5257359 0.4253227 0.3090114 -0.8506542 -0.5257298 0 -0.8506517 -0.6881894 -0.4999969 -0.5257362 -0.1624554 0.4999952 -0.8506544 -0.6881894 0.4999969 -0.5257362 0.2628688 0.8090116 -0.5257377 0.9510579 -0.3090126 0 0.9510579 0.3090126 0 0 -1 0 0.5877856 -0.8090167 0 -0.9510579 -0.3090126 0 -0.5877856 -0.8090167 0 -0.5877856 0.8090167 0 -0.9510579 0.3090126 0 0.5877856 0.8090167 0 0 1 0 0.6881894 -0.4999969 0.5257362 -0.2628688 -0.8090116 0.5257377 -0.8506479 0 0.5257359 -0.2628688 0.8090116 0.5257377 0.6881894 0.4999969 0.5257362 0.1624554 -0.4999952 0.8506544 0.5257298 0 0.8506517 -0.4253227 -0.3090114 0.8506542 -0.4253227 0.3090114 0.8506542 0.1624554 0.4999952 0.8506544 + + + + + + + + + + 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616991 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.1875942 0.5773453 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 + + + + + + + + + + 0.1891562 0.4353454 0.2201194 0.3408505 0.2923961 0.4111046 0.1891562 0.4353454 0.2923961 0.4111046 0.2610388 0.5272238 0.06585121 0.2636542 0.1202549 0.3546833 1.02655e-4 0.3599037 0.1891674 0.09197556 0.120263 0.1726311 0.07693755 0.06042814 0.3900915 0.1577098 0.2924051 0.1162222 0.3863589 0.0410583 0.3900838 0.3696232 0.3989981 0.2636671 0.4998953 0.3296785 0.6099237 0.1577003 0.7076132 0.1162198 0.6911104 0.216345 0.6099155 0.3696137 0.6010091 0.2636568 0.6911069 0.3109756 0.8108381 0.4353509 0.7076001 0.4111025 0.779882 0.3408538 0.934156 0.263669 0.8797454 0.354694 0.833759 0.263666 0.8108526 0.09198099 0.8797511 0.1726418 0.7798885 0.1864736 0.7798885 0.1864736 0.8797511 0.1726418 0.833759 0.263666 0.8797511 0.1726418 0.934156 0.263669 0.833759 0.263666 0.833759 0.263666 0.8797454 0.354694 0.779882 0.3408538 0.8797454 0.354694 0.8108381 0.4353509 0.779882 0.3408538 0.779882 0.3408538 0.7076001 0.4111025 0.6911069 0.3109756 0.7076001 0.4111025 0.6099155 0.3696137 0.6911069 0.3109756 0.6911069 0.3109756 0.6010091 0.2636568 0.6911104 0.216345 0.6010091 0.2636568 0.6099237 0.1577003 0.6911104 0.216345 0.6911104 0.216345 0.7076132 0.1162198 0.7798885 0.1864736 0.7076132 0.1162198 0.8108526 0.09198099 0.7798885 0.1864736 0.923085 0.06044203 0.8797511 0.1726418 0.8108526 0.09198099 0.923085 0.06044203 0.9998974 0.16742 0.8797511 0.1726418 0.9998974 0.16742 0.934156 0.263669 0.8797511 0.1726418 0.9998974 0.3599234 0.8797454 0.354694 0.934156 0.263669 0.9998974 0.3599234 0.923074 0.4668999 0.8797454 0.354694 0.923074 0.4668999 0.8108381 0.4353509 0.8797454 0.354694 0.7389487 0.527224 0.7076001 0.4111025 0.8108381 0.4353509 0.7389487 0.527224 0.6136447 0.4862654 0.7076001 0.4111025 0.6136447 0.4862654 0.6099155 0.3696137 0.7076001 0.4111025 0.5001069 0.3296607 0.6010091 0.2636568 0.6099155 0.3696137 0.5001069 0.3296607 0.5001106 0.1976431 0.6010091 0.2636568 0.5001106 0.1976431 0.6099237 0.1577003 0.6010091 0.2636568 0.613665 0.041049 0.7076132 0.1162198 0.6099237 0.1577003 0.613665 0.041049 0.7389741 1.02655e-4 0.7076132 0.1162198 0.7389741 1.02655e-4 0.8108526 0.09198099 0.7076132 0.1162198 0.4998953 0.3296785 0.3989981 0.2636671 0.4999017 0.1976609 0.3989981 0.2636671 0.3900915 0.1577098 0.4999017 0.1976609 0.3863589 0.0410583 0.2924051 0.1162222 0.2610529 1.02655e-4 0.2924051 0.1162222 0.1891674 0.09197556 0.2610529 1.02655e-4 0.07693755 0.06042814 0.120263 0.1726311 1.17172e-4 0.1674003 0.120263 0.1726311 0.06585121 0.2636542 1.17172e-4 0.1674003 1.02655e-4 0.3599037 0.1202549 0.3546833 0.07691794 0.466886 0.1202549 0.3546833 0.1891562 0.4353454 0.07691794 0.466886 0.2610388 0.5272238 0.2923961 0.4111046 0.3863458 0.4862747 0.2923961 0.4111046 0.3900838 0.3696232 0.3863458 0.4862747 0.3088967 0.3109791 0.3989981 0.2636671 0.3900838 0.3696232 0.3088967 0.3109791 0.3089004 0.2163485 0.3989981 0.2636671 0.3089004 0.2163485 0.3900915 0.1577098 0.3989981 0.2636671 0.3089004 0.2163485 0.2924051 0.1162222 0.3900915 0.1577098 0.3089004 0.2163485 0.2201245 0.1864705 0.2924051 0.1162222 0.2201245 0.1864705 0.1891674 0.09197556 0.2924051 0.1162222 0.2201245 0.1864705 0.120263 0.1726311 0.1891674 0.09197556 0.2201245 0.1864705 0.1662483 0.2636588 0.120263 0.1726311 0.1662483 0.2636588 0.06585121 0.2636542 0.120263 0.1726311 0.2923961 0.4111046 0.3088967 0.3109791 0.3900838 0.3696232 0.2923961 0.4111046 0.2201194 0.3408505 0.3088967 0.3109791 0.1662483 0.2636588 0.1202549 0.3546833 0.06585121 0.2636542 0.1662483 0.2636588 0.2201194 0.3408505 0.1202549 0.3546833 0.2201194 0.3408505 0.1891562 0.4353454 0.1202549 0.3546833 0.2038319 0.6020938 0.07803589 0.775239 2.88122e-4 0.5359594 0.2038319 0.6020938 2.88122e-4 0.5359594 0.2038319 0.3880734 0.2038319 0.6020938 0.2038319 0.3880734 0.4073758 0.5359594 0.2038319 0.6020938 0.4073758 0.5359594 0.329628 0.775239 0.8690316 0.364753 0.791095 0.5663278 0.6529134 0.364753 0.5301482 0.1791203 0.6523408 0.3573763 0.407952 0.3573763 0.5301446 0.5663277 0.407952 0.3880734 0.6523372 0.3880734 0.20244 0.9919336 0.20244 0.7758153 0.4040137 0.9139961 0.4079523 0.566904 0.6095281 0.6448412 0.407952 0.7830209 0.203883 2.88122e-4 0.4073758 0.1482443 0.2038091 0.2143085 0.4073758 0.1482443 0.3295453 0.3874972 0.2038091 0.2143085 0.3295453 0.3874972 0.07795333 0.3874103 0.2038091 0.2143085 0.07795333 0.3874103 2.88122e-4 0.1481037 0.2038091 0.2143085 2.88122e-4 0.1481037 0.203883 2.88122e-4 0.2038091 0.2143085 0.6523409 0.178544 0.407952 0.1785441 0.5301446 2.88122e-4 0.7910969 0.2018641 0.6529171 2.88122e-4 0.8690339 2.88122e-4 0.8116793 0.6448401 0.6101047 0.7830221 0.6101044 0.566904 2.88169e-4 0.7758153 0.2018638 0.9139932 2.88122e-4 0.9919315 0.407952 0.7835971 0.6095241 0.9217739 0.407952 0.999712 0.07803589 0.775239 0.2038319 0.6020938 0.329628 0.775239 + + + + + + + + + + + + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

1 0 0 13 0 1 15 0 2 1 1 3 15 1 4 22 1 5 2 2 6 14 2 7 24 2 8 3 3 9 18 3 10 26 3 11 4 4 12 20 4 13 28 4 14 5 5 15 21 5 16 30 5 17 6 6 18 32 6 19 37 6 20 7 7 21 33 7 22 39 7 23 8 8 24 34 8 25 40 8 26 9 9 27 35 9 28 41 9 29 10 10 30 36 10 31 38 10 32 38 11 33 36 11 34 41 11 35 36 12 36 9 12 37 41 12 38 41 13 39 35 13 40 40 13 41 35 14 42 8 14 43 40 14 44 40 15 45 34 15 46 39 15 47 34 16 48 7 16 49 39 16 50 39 17 51 33 17 52 37 17 53 33 18 54 6 18 55 37 18 56 37 19 57 32 19 58 38 19 59 32 20 60 10 20 61 38 20 62 23 21 63 36 21 64 10 21 65 23 22 66 30 22 67 36 22 68 30 23 69 9 23 70 36 23 71 31 24 72 35 24 73 9 24 74 31 25 75 28 25 76 35 25 77 28 26 78 8 26 79 35 26 80 29 27 81 34 27 82 8 27 83 29 28 84 26 28 85 34 28 86 26 29 87 7 29 88 34 29 89 27 30 90 33 30 91 7 30 92 27 31 93 24 31 94 33 31 95 24 32 96 6 32 97 33 32 98 25 33 99 32 33 100 6 33 101 25 34 102 22 34 103 32 34 104 22 35 105 10 35 106 32 35 107 30 36 108 21 36 109 31 36 110 21 37 111 4 37 112 31 37 113 28 38 114 20 38 115 29 38 116 20 39 117 3 39 118 29 39 119 26 40 120 18 40 121 27 40 122 18 41 123 2 41 124 27 41 125 24 42 126 14 42 127 25 42 128 14 43 129 1 43 130 25 43 131 22 44 132 15 44 133 23 44 134 15 45 135 5 45 136 23 45 137 16 46 138 21 46 139 5 46 140 16 47 141 19 47 142 21 47 143 19 48 144 4 48 145 21 48 146 19 49 147 20 49 148 4 49 149 19 50 150 17 50 151 20 50 152 17 51 153 3 51 154 20 51 155 17 52 156 18 52 157 3 52 158 17 53 159 12 53 160 18 53 161 12 54 162 2 54 163 18 54 164 15 55 165 16 55 166 5 55 167 15 56 168 13 56 169 16 56 170 12 57 171 14 57 172 2 57 173 12 58 174 13 58 175 14 58 176 13 59 177 1 59 178 14 59 179

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

0 60 180 13 60 181 12 60 182 0 61 183 12 61 184 17 61 185 0 62 186 17 62 187 19 62 188 0 63 189 19 63 190 16 63 191 1 64 192 22 64 193 25 64 194 2 65 195 24 65 196 27 65 197 3 66 198 26 66 199 29 66 200 4 67 201 28 67 202 31 67 203 5 68 204 30 68 205 23 68 206 38 69 207 41 69 208 11 69 209 41 70 210 40 70 211 11 70 212 40 71 213 39 71 214 11 71 215 39 72 216 37 72 217 11 72 218 37 73 219 38 73 220 11 73 221 30 74 222 31 74 223 9 74 224 28 75 225 29 75 226 8 75 227 26 76 228 27 76 229 7 76 230 24 77 231 25 77 232 6 77 233 22 78 234 23 78 235 10 78 236 13 79 237 0 79 238 16 79 239

+
+
+
+
+ + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/main/resources/meshes/wind_walker_pickup.dae b/src/main/resources/meshes/wind_walker_pickup.dae new file mode 100644 index 00000000..62292f0e --- /dev/null +++ b/src/main/resources/meshes/wind_walker_pickup.dae @@ -0,0 +1,147 @@ + + + + + Blender User + Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22 + + 2017-09-19T15:46:34 + 2017-09-19T15:46:34 + + Z_UP + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.03920602 0.04758029 0.8 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.64 0.5287738 0.5454956 1 + + + 0.25 0.25 0.25 1 + + + 50 + + + 1 + + + + + + + + + + + + + + + + + + + 0 0 -1 0.7236073 -0.5257253 -0.4472195 -0.276388 -0.8506492 -0.4472199 -0.8944262 0 -0.4472156 -0.276388 0.8506492 -0.4472199 0.7236073 0.5257253 -0.4472195 0.276388 -0.8506492 0.4472199 -0.7236073 -0.5257253 0.4472195 -0.7236073 0.5257253 0.4472195 0.276388 0.8506492 0.4472199 0.8944262 0 0.4472156 0 0 1 -0.1624554 -0.4999952 -0.8506544 0.4253227 -0.3090114 -0.8506542 0.2628688 -0.8090116 -0.5257377 0.8506479 0 -0.5257359 0.4253227 0.3090114 -0.8506542 -0.5257298 0 -0.8506517 -0.6881894 -0.4999969 -0.5257362 -0.1624554 0.4999952 -0.8506544 -0.6881894 0.4999969 -0.5257362 0.2628688 0.8090116 -0.5257377 0.9510579 -0.3090126 0 0.9510579 0.3090126 0 0 -1 0 0.5877856 -0.8090167 0 -0.9510579 -0.3090126 0 -0.5877856 -0.8090167 0 -0.5877856 0.8090167 0 -0.9510579 0.3090126 0 0.5877856 0.8090167 0 0 1 0 0.6881894 -0.4999969 0.5257362 -0.2628688 -0.8090116 0.5257377 -0.8506479 0 0.5257359 -0.2628688 0.8090116 0.5257377 0.6881894 0.4999969 0.5257362 0.1624554 -0.4999952 0.8506544 0.5257298 0 0.8506517 -0.4253227 -0.3090114 0.8506542 -0.4253227 0.3090114 0.8506542 0.1624554 0.4999952 0.8506544 + + + + + + + + + + 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616991 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.1875942 0.5773453 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 + + + + + + + + + + 0.1891562 0.4353454 0.2201194 0.3408505 0.2923961 0.4111046 0.1891562 0.4353454 0.2923961 0.4111046 0.2610388 0.5272238 0.06585121 0.2636542 0.1202549 0.3546833 1.02655e-4 0.3599037 0.1891674 0.09197556 0.120263 0.1726311 0.07693755 0.06042814 0.3900915 0.1577098 0.2924051 0.1162222 0.3863589 0.0410583 0.3900838 0.3696232 0.3989981 0.2636671 0.4998953 0.3296785 0.6099237 0.1577003 0.7076132 0.1162198 0.6911104 0.216345 0.6099155 0.3696137 0.6010091 0.2636568 0.6911069 0.3109756 0.8108381 0.4353509 0.7076001 0.4111025 0.779882 0.3408538 0.934156 0.263669 0.8797454 0.354694 0.833759 0.263666 0.8108526 0.09198099 0.8797511 0.1726418 0.7798885 0.1864736 0.7798885 0.1864736 0.8797511 0.1726418 0.833759 0.263666 0.8797511 0.1726418 0.934156 0.263669 0.833759 0.263666 0.833759 0.263666 0.8797454 0.354694 0.779882 0.3408538 0.8797454 0.354694 0.8108381 0.4353509 0.779882 0.3408538 0.779882 0.3408538 0.7076001 0.4111025 0.6911069 0.3109756 0.7076001 0.4111025 0.6099155 0.3696137 0.6911069 0.3109756 0.6911069 0.3109756 0.6010091 0.2636568 0.6911104 0.216345 0.6010091 0.2636568 0.6099237 0.1577003 0.6911104 0.216345 0.6911104 0.216345 0.7076132 0.1162198 0.7798885 0.1864736 0.7076132 0.1162198 0.8108526 0.09198099 0.7798885 0.1864736 0.923085 0.06044203 0.8797511 0.1726418 0.8108526 0.09198099 0.923085 0.06044203 0.9998974 0.16742 0.8797511 0.1726418 0.9998974 0.16742 0.934156 0.263669 0.8797511 0.1726418 0.9998974 0.3599234 0.8797454 0.354694 0.934156 0.263669 0.9998974 0.3599234 0.923074 0.4668999 0.8797454 0.354694 0.923074 0.4668999 0.8108381 0.4353509 0.8797454 0.354694 0.7389487 0.527224 0.7076001 0.4111025 0.8108381 0.4353509 0.7389487 0.527224 0.6136447 0.4862654 0.7076001 0.4111025 0.6136447 0.4862654 0.6099155 0.3696137 0.7076001 0.4111025 0.5001069 0.3296607 0.6010091 0.2636568 0.6099155 0.3696137 0.5001069 0.3296607 0.5001106 0.1976431 0.6010091 0.2636568 0.5001106 0.1976431 0.6099237 0.1577003 0.6010091 0.2636568 0.613665 0.041049 0.7076132 0.1162198 0.6099237 0.1577003 0.613665 0.041049 0.7389741 1.02655e-4 0.7076132 0.1162198 0.7389741 1.02655e-4 0.8108526 0.09198099 0.7076132 0.1162198 0.4998953 0.3296785 0.3989981 0.2636671 0.4999017 0.1976609 0.3989981 0.2636671 0.3900915 0.1577098 0.4999017 0.1976609 0.3863589 0.0410583 0.2924051 0.1162222 0.2610529 1.02655e-4 0.2924051 0.1162222 0.1891674 0.09197556 0.2610529 1.02655e-4 0.07693755 0.06042814 0.120263 0.1726311 1.17172e-4 0.1674003 0.120263 0.1726311 0.06585121 0.2636542 1.17172e-4 0.1674003 1.02655e-4 0.3599037 0.1202549 0.3546833 0.07691794 0.466886 0.1202549 0.3546833 0.1891562 0.4353454 0.07691794 0.466886 0.2610388 0.5272238 0.2923961 0.4111046 0.3863458 0.4862747 0.2923961 0.4111046 0.3900838 0.3696232 0.3863458 0.4862747 0.3088967 0.3109791 0.3989981 0.2636671 0.3900838 0.3696232 0.3088967 0.3109791 0.3089004 0.2163485 0.3989981 0.2636671 0.3089004 0.2163485 0.3900915 0.1577098 0.3989981 0.2636671 0.3089004 0.2163485 0.2924051 0.1162222 0.3900915 0.1577098 0.3089004 0.2163485 0.2201245 0.1864705 0.2924051 0.1162222 0.2201245 0.1864705 0.1891674 0.09197556 0.2924051 0.1162222 0.2201245 0.1864705 0.120263 0.1726311 0.1891674 0.09197556 0.2201245 0.1864705 0.1662483 0.2636588 0.120263 0.1726311 0.1662483 0.2636588 0.06585121 0.2636542 0.120263 0.1726311 0.2923961 0.4111046 0.3088967 0.3109791 0.3900838 0.3696232 0.2923961 0.4111046 0.2201194 0.3408505 0.3088967 0.3109791 0.1662483 0.2636588 0.1202549 0.3546833 0.06585121 0.2636542 0.1662483 0.2636588 0.2201194 0.3408505 0.1202549 0.3546833 0.2201194 0.3408505 0.1891562 0.4353454 0.1202549 0.3546833 0.2038319 0.6020938 0.07803589 0.775239 2.88122e-4 0.5359594 0.2038319 0.6020938 2.88122e-4 0.5359594 0.2038319 0.3880734 0.2038319 0.6020938 0.2038319 0.3880734 0.4073758 0.5359594 0.2038319 0.6020938 0.4073758 0.5359594 0.329628 0.775239 0.8690316 0.364753 0.791095 0.5663278 0.6529134 0.364753 0.5301482 0.1791203 0.6523408 0.3573763 0.407952 0.3573763 0.5301446 0.5663277 0.407952 0.3880734 0.6523372 0.3880734 0.20244 0.9919336 0.20244 0.7758153 0.4040137 0.9139961 0.4079523 0.566904 0.6095281 0.6448412 0.407952 0.7830209 0.203883 2.88122e-4 0.4073758 0.1482443 0.2038091 0.2143085 0.4073758 0.1482443 0.3295453 0.3874972 0.2038091 0.2143085 0.3295453 0.3874972 0.07795333 0.3874103 0.2038091 0.2143085 0.07795333 0.3874103 2.88122e-4 0.1481037 0.2038091 0.2143085 2.88122e-4 0.1481037 0.203883 2.88122e-4 0.2038091 0.2143085 0.6523409 0.178544 0.407952 0.1785441 0.5301446 2.88122e-4 0.7910969 0.2018641 0.6529171 2.88122e-4 0.8690339 2.88122e-4 0.8116793 0.6448401 0.6101047 0.7830221 0.6101044 0.566904 2.88169e-4 0.7758153 0.2018638 0.9139932 2.88122e-4 0.9919315 0.407952 0.7835971 0.6095241 0.9217739 0.407952 0.999712 0.07803589 0.775239 0.2038319 0.6020938 0.329628 0.775239 + + + + + + + + + + + + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

1 0 0 13 0 1 15 0 2 1 1 3 15 1 4 22 1 5 2 2 6 14 2 7 24 2 8 3 3 9 18 3 10 26 3 11 4 4 12 20 4 13 28 4 14 5 5 15 21 5 16 30 5 17 6 6 18 32 6 19 37 6 20 7 7 21 33 7 22 39 7 23 8 8 24 34 8 25 40 8 26 9 9 27 35 9 28 41 9 29 10 10 30 36 10 31 38 10 32 38 11 33 36 11 34 41 11 35 36 12 36 9 12 37 41 12 38 41 13 39 35 13 40 40 13 41 35 14 42 8 14 43 40 14 44 40 15 45 34 15 46 39 15 47 34 16 48 7 16 49 39 16 50 39 17 51 33 17 52 37 17 53 33 18 54 6 18 55 37 18 56 37 19 57 32 19 58 38 19 59 32 20 60 10 20 61 38 20 62 23 21 63 36 21 64 10 21 65 23 22 66 30 22 67 36 22 68 30 23 69 9 23 70 36 23 71 31 24 72 35 24 73 9 24 74 31 25 75 28 25 76 35 25 77 28 26 78 8 26 79 35 26 80 29 27 81 34 27 82 8 27 83 29 28 84 26 28 85 34 28 86 26 29 87 7 29 88 34 29 89 27 30 90 33 30 91 7 30 92 27 31 93 24 31 94 33 31 95 24 32 96 6 32 97 33 32 98 25 33 99 32 33 100 6 33 101 25 34 102 22 34 103 32 34 104 22 35 105 10 35 106 32 35 107 30 36 108 21 36 109 31 36 110 21 37 111 4 37 112 31 37 113 28 38 114 20 38 115 29 38 116 20 39 117 3 39 118 29 39 119 26 40 120 18 40 121 27 40 122 18 41 123 2 41 124 27 41 125 24 42 126 14 42 127 25 42 128 14 43 129 1 43 130 25 43 131 22 44 132 15 44 133 23 44 134 15 45 135 5 45 136 23 45 137 16 46 138 21 46 139 5 46 140 16 47 141 19 47 142 21 47 143 19 48 144 4 48 145 21 48 146 19 49 147 20 49 148 4 49 149 19 50 150 17 50 151 20 50 152 17 51 153 3 51 154 20 51 155 17 52 156 18 52 157 3 52 158 17 53 159 12 53 160 18 53 161 12 54 162 2 54 163 18 54 164 15 55 165 16 55 166 5 55 167 15 56 168 13 56 169 16 56 170 12 57 171 14 57 172 2 57 173 12 58 174 13 58 175 14 58 176 13 59 177 1 59 178 14 59 179

+
+ + + + + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 +

0 60 180 13 60 181 12 60 182 0 61 183 12 61 184 17 61 185 0 62 186 17 62 187 19 62 188 0 63 189 19 63 190 16 63 191 1 64 192 22 64 193 25 64 194 2 65 195 24 65 196 27 65 197 3 66 198 26 66 199 29 66 200 4 67 201 28 67 202 31 67 203 5 68 204 30 68 205 23 68 206 38 69 207 41 69 208 11 69 209 41 70 210 40 70 211 11 70 212 40 71 213 39 71 214 11 71 215 39 72 216 37 72 217 11 72 218 37 73 219 38 73 220 11 73 221 30 74 222 31 74 223 9 74 224 28 75 225 29 75 226 8 75 227 26 76 228 27 76 229 7 76 230 24 77 231 25 77 232 6 77 233 22 78 234 23 78 235 10 78 236 13 79 237 0 79 238 16 79 239

+
+
+
+
+ + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index fba9bb27..a2c22e09 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -16,7 +16,7 @@ From 52d3cea592b487ea4af368ec50ee3af4367bfd98 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 19 Sep 2017 23:04:17 +1200 Subject: [PATCH 02/32] Added preliminary icons for pickups RaceView now has a grid pane to contain some icons to display power ups. These are just preliminary ClientYacht now has a power up field that is set from recieveing messages in the Game Client, as well as observed by the RaceViewController to display the relevant icon when the powerup field is changed #story[1245] --- .../java/seng302/gameServer/GameState.java | 4 +- src/main/java/seng302/model/ClientYacht.java | 27 ++++++++ .../java/seng302/visualiser/GameClient.java | 33 ++++++---- .../controllers/RaceViewController.java | 29 +++++++++ src/main/resources/views/RaceView.fxml | 61 +++++++++++++++++++ 5 files changed, 139 insertions(+), 15 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index c5317fa6..be7039fa 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -47,6 +47,7 @@ public class GameState implements Runnable { private static final Double BOUNCE_DISTANCE_MARK = 20.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0; private static final Double COLLISION_VELOCITY_PENALTY = 0.3; + private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; private static Long previousUpdateTime; public static Double windDirection; @@ -453,8 +454,7 @@ public class GameState implements Runnable { Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier; if (yacht.getPowerUp() != null) { if (yacht.getPowerUp().equals(TokenType.BOOST)) { - // TODO: 11/09/17 wmu16 CHANGE THIS TO MAGIC NUMBER - maxBoatSpeed *= 2; + maxBoatSpeed *= VELOCITY_BOOST_MULTIPLIER; } } diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 89715852..b1996333 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -12,9 +12,13 @@ import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyIntegerWrapper; import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongWrapper; +import javafx.beans.value.ObservableObjectValue; +import javafx.collections.FXCollections; import javafx.scene.paint.Color; +import jdk.nashorn.internal.objects.annotations.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import seng302.model.token.TokenType; /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) @@ -34,6 +38,12 @@ public class ClientYacht extends Observable { void notifyRounding(ClientYacht yacht, int legNumber); } + @FunctionalInterface + public interface PowerUpListener { + + void notifyPowerUp(ClientYacht yacht, TokenType tokenType); + } + private Logger logger = LoggerFactory.getLogger(ClientYacht.class); @@ -44,6 +54,7 @@ public class ClientYacht extends Observable { private String boatName; private String country; private Integer position; + private TokenType powerUp; private Long estimateTimeAtFinish; private Boolean sailIn = false; @@ -58,6 +69,7 @@ public class ClientYacht extends Observable { private List locationListeners = new ArrayList<>(); private List markRoundingListeners = new ArrayList<>(); + private List powerUpListeners = new ArrayList<>(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); @@ -199,6 +211,17 @@ public class ClientYacht extends Observable { this.position = position; } + public void setPowerUp(TokenType tokenType) { + this.powerUp = tokenType; + for (PowerUpListener listener : powerUpListeners) { + listener.notifyPowerUp(this, tokenType); + } + } + + public TokenType getPowerUp() { + return powerUp; + } + public void toggleSail() { sailIn = !sailIn; } @@ -268,6 +291,10 @@ public class ClientYacht extends Observable { markRoundingListeners.add(listener); } + public void addPowerUpListener(PowerUpListener listener) { + powerUpListeners.add(listener); + } + public void removeMarkRoundingListener(MarkRoundingListener listener) { markRoundingListeners.remove(listener); } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 5cab5040..10a682fa 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -37,6 +37,7 @@ import seng302.model.stream.parser.RaceStatusData; import seng302.model.stream.parser.YachtEventData; import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RegattaXMLData; +import seng302.model.token.TokenType; import seng302.utilities.Sounds; import seng302.utilities.StreamParser; import seng302.utilities.XMLGenerator; @@ -245,7 +246,7 @@ public class GameClient { break; case YACHT_EVENT_CODE: - displayYachtEvent(StreamParser.extractYachtEventCode(packet)); + processYachtEvent(StreamParser.extractYachtEventCode(packet)); break; case CHATTER_TEXT: @@ -408,19 +409,25 @@ public class GameClient { * * @param yachtEventData The YachtEvent data packet */ - private void displayYachtEvent(YachtEventData yachtEventData) { + private void processYachtEvent(YachtEventData yachtEventData) { if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { showCollisionAlert(yachtEventData); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { - showPickUp(yachtEventData); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_BUMPER.getCode()) { - showPickUp(yachtEventData); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_HANDLING.getCode()) { - showPickUp(yachtEventData); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_RANDOM.getCode()) { - showPickUp(yachtEventData); - } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_WIND_WALKER.getCode()) { - showPickUp(yachtEventData); + } else { + TokenType tokenType = null; + if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { + tokenType = TokenType.BOOST; + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_BUMPER.getCode()) { + tokenType = TokenType.BUMPER; + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_HANDLING.getCode()) { + tokenType = TokenType.HANDLING; + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_RANDOM.getCode()) { + tokenType = TokenType.RANDOM; + } else if (yachtEventData.getEventId() == YachtEventType.TOKEN_WIND_WALKER.getCode()) { + tokenType = TokenType.WIND_WALKER; + } + + showTokenPickUp(tokenType); + allBoatsMap.get(yachtEventData.getSubjectId().intValue()).setPowerUp(tokenType); } } @@ -437,7 +444,7 @@ public class GameClient { } // TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user - private void showPickUp(YachtEventData yachtEventData) { + private void showTokenPickUp(TokenType tokenType) { Sounds.playTokenPickupSound(); } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 2ac06dfb..15c3d8ec 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -20,6 +20,8 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Point2D; +import javafx.scene.Group; +import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.SubScene; import javafx.scene.chart.LineChart; @@ -47,10 +49,12 @@ import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.stage.StageStyle; import seng302.model.ClientYacht; +import seng302.model.ClientYacht.PowerUpListener; import seng302.model.RaceState; import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; import seng302.model.stream.xml.parser.RaceXMLData; +import seng302.model.token.TokenType; import seng302.utilities.Sounds; import seng302.visualiser.GameView3D; import seng302.visualiser.controllers.annotations.ImportantAnnotationController; @@ -60,6 +64,8 @@ import seng302.visualiser.controllers.dialogs.FinishDialogController; import seng302.visualiser.fxObjects.ChatHistory; import seng302.visualiser.fxObjects.assets_2D.WindArrow; import seng302.visualiser.fxObjects.assets_3D.BoatObject; +import seng302.visualiser.fxObjects.assets_3D.ModelFactory; +import seng302.visualiser.fxObjects.assets_3D.ModelType; /** * Controller class that manages the display of a race @@ -110,6 +116,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private Label windSpeedLabel; @FXML private Label positionLabel, boatSpeedLabel, boatHeadingLabel; + @FXML + private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon; //Race Data private Map participants; @@ -222,6 +230,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel return finishScreenDialog; } + public void loadRace ( Map participants, RaceXMLData raceData, RaceState raceState, ClientYacht player) { @@ -241,6 +250,25 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } }); + player.addPowerUpListener((yacht, tokenType) -> { + if (yacht == player) { + switch (tokenType) { + case BOOST: + velocityIcon.setVisible(true); + break; + case HANDLING: + handlingIcon.setVisible(true); + break; + case WIND_WALKER: + windWalkerIcon.setVisible(true); + break; + case BUMPER: + bumperIcon.setVisible(true); + break; + } + } + }); + updateOrder(raceState.getPlayerPositions()); gameView = new GameView3D(); // gameView.setFrameRateFXText(fpsDisplay); @@ -279,6 +307,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel }); } + /** * The important annotations have been changed, update this view * diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index a2c22e09..03728bde 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -1,5 +1,14 @@ + + + + + + + + + @@ -14,6 +23,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 6cde0164011e060d42a596e40ce96850e25184c7 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 20 Sep 2017 12:02:12 +1200 Subject: [PATCH 03/32] Fixed random token assigning and realisation Token class now has two functions: assignRandomType and realiseRandomType The former can be used to assign any random type to the token including the random type The latter can be used to assign a concrete random type to the token (not the random type) #story[1245] --- .../java/seng302/gameServer/GameState.java | 10 ++++---- src/main/java/seng302/model/token/Token.java | 23 +++++++++++++++++-- .../java/seng302/model/token/TokenType.java | 15 ++---------- .../java/seng302/visualiser/GameClient.java | 10 ++++++++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index be7039fa..1dd9963a 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -283,10 +283,9 @@ public class GameState implements Runnable { Random random = new Random(); tokensInPlay.clear(); - //Get a random token location + //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); - //Set a random type to this token - token.setTokenType(TokenType.values()[random.nextInt(TokenType.values().length)]); + token.assignRandomType(); tokensInPlay.add(token); } @@ -389,6 +388,7 @@ public class GameState implements Runnable { //Yacht Collision ServerYacht collidedYacht = checkYachtCollision(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht); + Token collidedToken = checkTokenPickUp(serverYacht); if (collidedYacht != null) { GeoPoint originalLocation = serverYacht.getLocation(); @@ -433,8 +433,10 @@ public class GameState implements Runnable { } //Token Collision - Token collidedToken = checkTokenPickUp(serverYacht); if (collidedToken != null) { + if (collidedToken.getTokenType() == TokenType.RANDOM) { + collidedToken.realiseRandom(); + } sendServerMessage(serverYacht.getSourceId(), serverYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType() .getName() + " token"); diff --git a/src/main/java/seng302/model/token/Token.java b/src/main/java/seng302/model/token/Token.java index 56536da3..fcdbab5e 100644 --- a/src/main/java/seng302/model/token/Token.java +++ b/src/main/java/seng302/model/token/Token.java @@ -1,5 +1,9 @@ package seng302.model.token; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; import seng302.model.GeoPoint; /** @@ -9,6 +13,7 @@ import seng302.model.GeoPoint; public class Token extends GeoPoint { private TokenType tokenType; + private Random random = new Random(); public Token(TokenType tokenType, double lat, double lng) { super(lat, lng); @@ -19,7 +24,21 @@ public class Token extends GeoPoint { return tokenType; } - public void setTokenType(TokenType tokenType) { - this.tokenType = tokenType; + /** + * Assigns a random type to the token (including the random type token) + */ + public void assignRandomType() { + tokenType = TokenType.values()[random.nextInt(TokenType.values().length)]; } + + /** + * Assigns a random, concrete type to the token (cannot be the random type) + */ + public void realiseRandom() { + List tokenTypeList = new ArrayList<>(Arrays.asList(TokenType.values())); + tokenTypeList.remove(TokenType.RANDOM); + tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size())); + } + + } diff --git a/src/main/java/seng302/model/token/TokenType.java b/src/main/java/seng302/model/token/TokenType.java index e85c6bec..82aecc97 100644 --- a/src/main/java/seng302/model/token/TokenType.java +++ b/src/main/java/seng302/model/token/TokenType.java @@ -8,8 +8,8 @@ public enum TokenType { BOOST(0, "Boost"), HANDLING(1, "Handling"), BUMPER(2, "Bumper"), - RANDOM(3, "Random"), - WIND_WALKER(4, "Wind Walker"); + WIND_WALKER(3, "Wind Walker"), + RANDOM(4, "Random"); private int value; private String name; @@ -26,15 +26,4 @@ public enum TokenType { public String getName() { return name; } - - public static TokenType getToken(int value) { - switch (value) { - case 0: - return BOOST; - case 1: - return HANDLING; - default: - return BOOST; - } - } } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 10a682fa..9abf282a 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -446,6 +446,16 @@ public class GameClient { // TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user private void showTokenPickUp(TokenType tokenType) { Sounds.playTokenPickupSound(); + switch (tokenType) { + case BOOST: + break; + case HANDLING: + break; + case WIND_WALKER: + break; + case BUMPER: + break; + } } private void formatAndSendChatMessage(String rawChat) { From 034e4c252a7347a7b888d50e7de72a4a197582a6 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 20 Sep 2017 13:09:09 +1200 Subject: [PATCH 04/32] Icons now display, blink when they are about to run off, and turn off after their time out TokenType Enum now also has a timeout construction field #story[1245] --- src/main/java/seng302/model/ClientYacht.java | 1 - .../java/seng302/model/token/TokenType.java | 19 +++-- .../controllers/RaceViewController.java | 76 ++++++++++++++----- 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index b1996333..5cedbfba 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -40,7 +40,6 @@ public class ClientYacht extends Observable { @FunctionalInterface public interface PowerUpListener { - void notifyPowerUp(ClientYacht yacht, TokenType tokenType); } diff --git a/src/main/java/seng302/model/token/TokenType.java b/src/main/java/seng302/model/token/TokenType.java index 82aecc97..8ae60dcc 100644 --- a/src/main/java/seng302/model/token/TokenType.java +++ b/src/main/java/seng302/model/token/TokenType.java @@ -5,18 +5,21 @@ package seng302.model.token; * Created by wmu16 on 28/08/17. */ public enum TokenType { - BOOST(0, "Boost"), - HANDLING(1, "Handling"), - BUMPER(2, "Bumper"), - WIND_WALKER(3, "Wind Walker"), - RANDOM(4, "Random"); + + BOOST(0, "Boost", 10_000), + HANDLING(1, "Handling", 10_000), + BUMPER(2, "Bumper", 10_000), + WIND_WALKER(3, "Wind Walker", 10_000), + RANDOM(4, "Random", 10_000); private int value; private String name; + private int timeout; - TokenType(int value, String name) { + TokenType(int value, String name, int timeout) { this.value = value; this.name = name; + this.timeout = timeout; } public int getValue() { @@ -26,4 +29,8 @@ public enum TokenType { public String getName() { return name; } + + public int getTimeout() { + return timeout; + } } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 15c3d8ec..e45dd091 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -73,6 +73,8 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType; public class RaceViewController extends Thread implements ImportantAnnotationDelegate { private final int CHAT_LIMIT = 128; + private static final Double ICON_BLINK_TIMEOUT_RATIO = 0.6; + private static final Integer ICON_BLINK_PERIOD = 500; @FXML private Pane basePane; @@ -250,24 +252,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } }); - player.addPowerUpListener((yacht, tokenType) -> { - if (yacht == player) { - switch (tokenType) { - case BOOST: - velocityIcon.setVisible(true); - break; - case HANDLING: - handlingIcon.setVisible(true); - break; - case WIND_WALKER: - windWalkerIcon.setVisible(true); - break; - case BUMPER: - bumperIcon.setVisible(true); - break; - } - } - }); + player.addPowerUpListener(this::displayPowerUpIcon); updateOrder(raceState.getPlayerPositions()); gameView = new GameView3D(); @@ -307,6 +292,61 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel }); } + /** + * Displays the relevant icon, starts blinking it when it is close to turning off and then + * switches it off after the tokens time out + * + * @param yacht The yacht only for which we are displaying the icon + * @param tokenType The type of token, indicating what icon needs to be displayed + */ + private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) { + if (yacht == player) { + final ImageView iconToDisplay; + + switch (tokenType) { + case BOOST: + iconToDisplay = velocityIcon; + break; + case HANDLING: + iconToDisplay = handlingIcon; + break; + case WIND_WALKER: + iconToDisplay = windWalkerIcon; + break; + case BUMPER: + iconToDisplay = bumperIcon; + break; + default: + iconToDisplay = velocityIcon; + } + + //Turn icon on + iconToDisplay.setVisible(true); + + //Start blinking icon towards end + Timer blinkingTimer = new Timer(); + blinkingTimer.schedule(new TimerTask() { + Boolean isVisible = true; + + @Override + public void run() { + isVisible = !isVisible; + 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()); + } + } + /** * The important annotations have been changed, update this view From 66d9a06f9ef6113579d0cd82a75b408ba5b33050 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 20 Sep 2017 17:19:14 +1200 Subject: [PATCH 05/32] Adding icon files to git #story[1245] --- src/main/resources/icons/bumperIcon.png | Bin 0 -> 3580 bytes src/main/resources/icons/handlingIcon.png | Bin 0 -> 3205 bytes src/main/resources/icons/velocity.png | Bin 0 -> 4328 bytes src/main/resources/icons/windWalkerIcon.png | Bin 0 -> 3939 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/icons/bumperIcon.png create mode 100644 src/main/resources/icons/handlingIcon.png create mode 100644 src/main/resources/icons/velocity.png create mode 100644 src/main/resources/icons/windWalkerIcon.png diff --git a/src/main/resources/icons/bumperIcon.png b/src/main/resources/icons/bumperIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1da6d4cf8d72949af1b64aa7e60335804d5d7b5 GIT binary patch literal 3580 zcmV-zfoUS3|&(a{?l8~6A3)z#I(!NC_77sbWJ%F4>SySq3zILyqgwv;+}wbGfG8*^Pft&8Z*QBMn`mfgtgNh_o}P}5jt>tH zA|fJ)h=^)xYD`Q_S65ekeSNgFw3CyQk=u-F000d$Nkl+ATH!mx*Y+tOB5Cdy#wp5EVKl((!BQ36B9Lj;YW z#D$fHzcX~kn4qC$eDY_8LrQQ_>dMLjjt^G_|NlcGqJ~kO!SH9X)mQv%xOuPcUhJn( z66A?)uh>@ho*=cD~Jz7!QO{|6DA4-0-n z0kqL??p6Q5?-keb=B|2zgrQZ*uLagiMBPe$453pYLQ$JwYChC#4w`!-68geY+_tu8OaAtyug z%JG`+SG#*>>D#|B1<9Tc%C5!VV)2iJ+oQ8o`GX-cm{+Awe**a!j@FpqMDhC_^!fTa zaB?vWV@zx88rVuFdgQXE-8};3~)0 zFOkzW)<268lCsezkKPX!NU-bcDpq9ZZ;2kw+3`GSNr;! zsMHv`q&j`eS9QP)RV%O`fD*&@h8T*R(uOl0x4*3MZ%|=qUNFH^-dqHB+Vbe%lA*$o z=GNrCmmhY5qyU=-^oPe1mRb-B3LPd z{4Scv$aL#-OBERI$QDYwXhs3~5+D zkXb@^L3WvMM6pr8HLfkwmdVg2hC=GC^+?!gO!>Ic#&6V?hstC~!}>__1)z$tPPhX; zdMB5+UNX`dy5tbb!ulR1I8LUuv7w2}TyjpNGW0FH`X7n6=28(AwQ>zJPI(nbWk?R8 zEJSyxk{OHAZ@9>@Epf$pk;agEYY9{S1~n7LS?CQs(7QD{-f0Y%BxI80e?d#LMAPEj zPK|D98pAza{Xo3@-Z_h43!mc}owgK)vCG3hnLt1v z>DGFS%YXce(t^)U+{qRR@0fv`FY6QuH1RntbHVvxhn*g;DFU)ZTTxh_Z6?sypwDiv_qY>8o`!}J~aK%GK+)e)*`{XK#NK&B?dM)+O_Apgn=jt2neR%d})(P zfNIgdDlCa5+z0|!VCYjXhL{c!b>3<7+bpV{=wp*Yp&>YgLr;yMzXOMracRQnHsP@A zRLU_TA)g@s3(SO<=BRXNt?CyrG6^jsiE)*EeJe_9V zB%%rRFmd^lipenv^=K}?EJu`U)bI0NV-!i6VpEV z$)vXiw5TU1XwTrn=X|o;-y1?ym2M1(mf0u50bmD(p3WQL1g?9Vlys`dAXZgf_ab^O znHe0?h(5(k^r4FJ9wH+U#pf=I^Yl_x5&VGHHb{i^K#MM<1eZI&&yd}p4*Me({mRUt zL$+zl3k}Gs4Ww$%WzN_|znKY3QiSR9)(I3}CfEHo+T>`}yDM6d@ zV&?j7V5>#7{DXT37GIw8CI?IWHuu`0{%K@6Z!l%(R@!LK@B!MVQRuORK#Ck)RP*8|o>;CX0+& z5rbJZaq7Oq1Pe!9$D9#wZO!^2*~OOwbH%c+=LW)-8P-r_7GK&ieR$1lz0=T(d1FaslsQG6jN;4k0VOo9?FwZkY+5K}EOl1#WyoA(JL+i!Ul z!W_b^;>!$5s2Ay~Vtx@2gSUr@BBS`Syg>=w>pb7vCNbR_ieg4qZO9~>j(U1A2nVvx z2)vqJe6g8OK2dwr3zQ1X1*I2X*sQg@Z&(ZXv}FcZ^wi>u-G|At1s_Y+iO}uw3eUU| zSp=dsT&EBW9)O==)z}%!!`^Kc8{TL|B>N6J%~OjnLnxsGos1y5;L3WAG_3!jyPH;g zv1c&Zv>0jRrjZ-G8?dM)jm+s@xG83$eT5a5SfdSW`Lv`(+akzq4_FpSE55i)prn(E zfdoONjuv5#WMfl`FTi~@bbCsPe}QW=;`dpk-WnA*dGW;zVII?6aZdPRcZ%8RiznbbQ1HWbq0%aZZ4lcnLB0Y)A@ zF9ggI)75YZMOu8hGaFcEzqSxy3*YwvX53i3AM`<4@ny*vL95E~ziI%d5CsA3E_x_r zk`-SZMnWqqw&wvod0wA?g^uHp6kqHJO6WAIdhtpKX+8yCas(7n9$Qj;2^l-Hl^8)Y zCrYOXQf8<-4rs#^F<)V>93Lzwltd$sFNf^ zlSZlIJY^{TmKQK|>*QJVVDW_#44Ce-z4*U9gmuFlC_Og^Hi@Vk(SJS-h-_wjrzaaT zP;3Tfjsf?|7*b7wDISu8RjHW>m}twAHwYDE_<_FmA(c6i(RhKF3Dbdo_By4g8x}f- zpP5GMP>SoM(RjU#JsE!olTFipN+ife{mM`tW|s>O$Z9tX!8Vx4jjNt$4D>* z_EkV)A*VRisAQ%F;G=BQjm8T%-I7h20G)LY6GR$f(_iBl&VsPP4nE68p&*z7IwtSL zx%$!Gvjl%*9_n`zqd~m^>&tWuVjwcskk_e(9(M3QPlrU^8%V@9X6ugE&zM6C$L%M-OY3aei_}UA)}vkB^ZEUkla;vK|e*C z^<EKRLJI&k1%iMD~M&eu8s{tv*CDC~-h{sV$!db)6oW*-ogG~wwFRfg&h zF4PUUiS8H3@p~_ zW^xhuWITc~SlwUYk>9_79`;pXl9^v!t*8gOZ`}`xEv`lXcg>aU>`FFp4qBpNIS3 z@q5Db+d-#4a)ZhrjnSY%g9Z&6G-%MEL4yVjD#kw&wXtdf*Mg$}0000T`2*BqSu!(a}6S zJWftdn3$Nix3^+qVt{~v!otEsLqnydrPtTjczAdtBO_K;R=>Z$92^{-ot;5JK@ANJ z7Z(?hkdSFw2O{5Y0BNC|3~?yaZzs@%`U$H5nbF zP+iqs1%BB*KPL|Kp)XZk*OwPbc%D*Tu`SE=pGTx+xh>8=@I=QT29;=CG|Lud_gFTC z)`=Q=z$7HPs-hKw++VDss?y2O1IIz{f;1u?1HEsAbu=`cdt?zCy9=TxTCIM;u{a+wFZ`jYouTwD7U6eB zywgL=NmWhkBm)-tq#BZ=SCxFHOBcej8ceOLQn~eci#V-@Avl`#!tadPXaIieX>a__ zn6CS!r!tye&4E?cRFZwxP9ufDW0&KPe@jGL?5$C4@SqX1K9tiJ7 z-Sm5eK4RL4UXpcxBCs1XIf`mAG`T-m2(8ovV>s&vO9>-@+Vn5vyRq7^PoozMB?Q|M z2h0}^FK{|z>bjm(KQTG|o693-ycE62mSDInE3K2{!KSTF|E6_SE^Tj^vxt+TogHS? ztc_lYvu9)7*lqDSA3+N#A1>Dv9nxRh%;l7cpQ3)@X+B<9yk{}ash@Jia-t?9+FSDp zY4&d6uc>ye5rbeP%K7oyP5a-hX?IU|*-s)EZVUHsniN~gmUN}4C`)vqc`m5FrYOK8 zx4lR>WFo~UpFBnIgRETMgg@KHT;#66??-!~;b8^+TU{_xyP&Awknf*ct{xA&QtG43 zeN7#)nyZ}xhgG(xrQDrcm$-dmrG_gc!L*XJ@cC6+Bu*Pe`ZC(!?t_7}1=9~uB=n)5u+#ua&G~$6P2^rXh z0<&odD+*)y&E-7^x7Z-ARd`deYRpd?^-J(X5wRH?>{blSVs_GhTvjqMj%pg<9)(yC z* zI}Sj>gd?chJgQ%yudp6E`=|{HoWzH4BKThT9Qx=zV0+8`JrVSV^1HBk0NNw&b2P!v zG05;SAdDKE!l%nvy@D`oaMM2HG4l-t4ZFVmWdfIsYiMEI=kRC%tv*z58O?YkW8Ha z6^VQ3L%5AXMQQx`@zMI|kB`qdjofB4ftKI?`ZMUzb;#XRD*6PTe8Fd=99TjY-mAQT zVjZkaz$zhRXuJj1ft!$l_!=v+1FDfs?B1)Zl9++AwJqENm?mB?w?x3^VV7X6(II!Y zr~|9Sdno1lk8LRdOdh$#;>q^J*!L^K(!y5a1Isl1J|-ap4gFS;HS_k96NPw0&tm{i z(?8~#3V0*+eR_$FdhI4jwI@KDTV-Mxl3wK%@2>Q|@K(?YoB^8K7egp$e2IUJN(@c` z5+aK90HO@P^a&`=5`B`Y&4D+^`(u=$l_@~zf5l`%QF_P^=@$oqDlX$AvW9{WJVe6` zahVLq#Tb2hhILISn6)a8C^Z0Bh3EedXy=Xj;mNVV)@OS^WSeuYrQT!*?2+`EPY{(N zfJnpB&Bz1(Y}$s&Hjk0g;Un<|-{P^!uLo?z#(VLAlL%D#tCWyA+Y+pa z38%aw1@&XVT(k6`zlQA$eLu-2q`cWt`;n26w7b{kF1cL?w+w-;tqlC{aNa}NlBiaH zj=SWyH^$r_Z%{ASr@j1e`oQbZ>;B_S1e?mTsyH6goG#VlefMHld;0TU>s3%3cUg8r z6!5fp&`~5_uVy9hz38Rvo#1wW= zx#9z3soOa2bOU4=;-ZXj`FFOEWr(|dLAM`wh%$UfGUPFSbU>8h0cURU4Dq6F5M_w1 zb0{h>L>Xdz9vjI)NHRowj|~B_ZjfYHW36S0Q%x)b{%7cd-_P*(@5A4Jmi`Ci;qTXJ ze!pJ&`|lS2f&1Z8-0^?Eo#yx3+J3*iRJw+|w9VUuEuu(-8>IXeSQL=&i3i-iCn7)( zDH9m|hZX=mq-7|bOG45YsiXpD;2h`f3iGo$7 zP*YqROKjo~XhglYR(#sE<}k%3GR2qP0#{e^0ik6JIa7QoUySe#tQM+m!zxpZFl(H* z3q{);IdhyhZ>;_W)P)*aw;VIa>T~B?d;?Qn@&=#QRVfhp7W_FgU(lq5YM-cz$eEGI zd-{4cfnY~L84NIaPa?T%r*(=qAGDSt!2y-KCY679M29n4AKA$K!mTaY7O4z-a?Ems>0!|A=DfQ1KO=k1thI0cQlV|AhFO z3SxCJFew=``^V$!Vu;l?!T5a~s`gTb4UWNn2pF%3L)l)+sHJKQAsE^}oK#~-s!o(7 zLt33Esajux42iYAr0TT^GNjgP6RNo<$B8niNXgoPN|)e#n{1uetSvKF*RJ#QI?w)MOvYO;$kw64i6QCDArp+jAL zf!+uL44rx-@b|aiXXx19g1tuxJ44qVCG34>xEZ?lnc?mg#LdvXR}goDk5Hca4=HQ{Oe--g%4+*?Q;k_CMrf$k_jowTC4eL)IRaoP9{S81nWZCD!bn}MNBUv9SE>dp+sdaJ*v09R5ktw4^B4E+k1 zTQ>_Vd`BLV;uMTrc4MeZv-0sx&??rz1HGm(d7uYXNXVRgH2Bc5Mo3l)%GRr7qL<2Wv!hKQH94z zO#B3?F7g$tW{nBIRO`kr+wCzZ$R1uqsBGO6X11zY2SubB-XAQLZHS_H<~#{8H^Fm% z2Zz*9cC0oM4gH9+UlJU0z72|^E=s~KnHB|%saPBo!>Fnlm1tcw81vk+DYQP)@*930Wn(Z|Qf@$vB}C@A{+`h$amBO@b9N=obN>)hPjadC03uCDd< z_1W3k;^N|*oSebI!L+orVPRocS65F@PdGR@dwY9DMMXV5J&lcxX=!Psq@)-a7?hNh zZEbCTe}51V5W__54gdfUE=fc|RCt{2UFm|NFcQWj+?U)Sm$bU?|A>o6w~z`X3JA07 z_iv_EQlwIMRV3+)FTVKVi!Z+T1Pou(oEySGQI#OP|3lM!W9_9)hJU3gZp{5LJ(Lx) ze&j2DEL4?;^td;h_)4$8_A=jt@2asE6={hrix7+vLc1s0e_>3BW#KX{A}?0me~iqq z-M6$vmQHY&E*20bx`oQL-Oq6CWedVrn@ehAol9GvP959nrBOG(m7%xK${fE$-IGe3 zm)YLi@K*XsdR^u_)ODytyENJ2P8ynWLGmQ(o>a={qG;RFBu>y|KV%N_9z>F#tO=a8 z$@)W>6uE_GBkvAuEU^BPi%ZR;cC-C^0W_EGu_#YD@VKN=XB z$_H(LFgg(N#Ua#s4>F}Nar-|ydF2?B!{@}RcNE>gmNwbSv;C>{08drt{|`;;J)ZVi zZppf+GR5fVeS|)@sDw5U5!uR$C~lqjTeu?0JAbRQ1#T70Q~ECHTH*iIIvPpfqvgSH%8+4}^juptT`aFV@U z?YM6b>-C~SH8^urMC;#`$4Fs)mqXzdN^~r_{-t#e7NgHdY<-+QJwKk}0p%<}VQWUj zr(@1BBy>AjNN+e18RbxdvpN};1VR+HaFaU2qp}Q>Xc03`DMkez*3U^WW?P9;*luKy zoJWs4A_emxOhcTFK2uIr&U+Q7u!Wa2|KW=87b$dqc#o8$*<32E<`THWDeqTar0~c$ zedW27e`tz$f?Ph0yXPwBephXafD5h|Nay{a6TIJm9O5ULk9() zCz?0fe&9SHy_S@|03tPKV_Fig+m0~eY`0POpK66tJ*2M4ev-KHFYI@RZU;%b^cI3j zo}CfcRgQ3#@;(>$l|8w%M5u=W_tCmcP#VU7nn3yege(1(=mn2s!lbE9u5sWLJr@R* zHzU+7ayE%Fk9S50Y}f?ivbmGuNBu*J<}nskDroi4>`qg%WrF;+b>r=rFO3}3-(L*+ zLY)+Is9>XSui^ApEEy_0#D{ioMyL|D=}YX6fNt{MqwG2SHmxVVQQkcN|5ALilG4l9y9{a2HzHLT04BA{Usn4iG!1f2)sS^OCzc|IHM zfNiavm|DYXSidz4l@-%NIg5hmk3@2wC$ZrEiA-cMtRFvOk%;h1M=@k-9z9~}o#t+D zBE5X$xU=BL*_}j!OcqlZ5lh_Q@`d+n6s(^ZN>dtMC0j&Cm-$Mzg1u!~x_%r;8v0CI zOE1I~f5c?;L3M6~if7jDoY8hC_M#eQ27aLxOfkXp-~JG@RJ}g#;Tp4tp+|qwNf}(m zH~k+7Q-M#bAWjsBo!o47cgxS#^&Sqear=6)QqEu`2O_)ehp_^tY~f3A|qMEH;roskILI9 zf`kYe`6;~~|4zWj7Fe?On}YJeO&rQmAaYo-PL~5lJZU(W z!7>saH14uFbIP-#0gN|jf;q)VRuW-e%5N$ zxA6XI@=Z1*NhhH9*_)`2Z{cklH|b+YK4TLZN6KeLpXhh9O-A*=Ch7&`wm__~+1sK> z)fj3YTBj$lg{1GtH-ZE9S{Ay$wgLD+}!`P|aa9(6H%y7{D?7tN#g`!;=H>OxdOP;3fsq zQR@hpP*4V76Dca_+hdyi(oBnZ+O{5sv~RYoG=m@9hIm7 zCPh$Tt_35kk@e78I3G@+ooJQVt>RD*qdUANG-a><7VVAusPLq9KJDfGzX{Yde*{LD z9OXX=f_BBIBnNEzjMqRpL|!axSOmTjWu3+nVn^dymj+Prrw>992hcy;bjyO4n0B`d ziKH<{&}p>g_SD`WV@O*(pGVjntDfDjk8|h^w7h&~6BBxSt z2XKUc`v-iGZ~9r&yf)O7n!SW@C1guEbXrM&)W?Lh5JL89j=bS`C=9E zaxR%u8uW_oDGkA=1BU2UuNczD(E6;K>qix0cBmu$<*=6uXu1A^N*zyBE!Xc3;0Gpw z(&f%!m57$_BcS8^@^!c9dgoh^?K23~Js!#>5N_`f10dvft&KKlQAz~$>r@zlms&&V zE~H=H;UtG{L?fml8MORaaPUMJC!Xge*CCg_ddh}pytTgkTZ_PP7<7xrH7{blo~(wy8`)#IJ#PJo^Pti47lU z_Y?3Mc@?OyLm8d>@&v-gbbFCjh^;qIPfWnadf!1kS?(}u>(~U6x3qldT{_DxXi0+$ ziE&0S`2$APR1-&V3&~q9O_-K>XSvXdz+g5gHuU5C?z_v###cXzIg$`=s>IMuao9o8-vkV0FX=k&s=wU0)>Nx$9Iqy zqfK3Ke)zSOMm5H!1EPs< z&q&UQpzeABH7O%CoZ3&Mbux9x^cb5?WK!vt4nI7Xdno7)!$+VsNgcXRgflpc%;xvU z7EX@|8QtZ&4hScnf%l}(-i&7qwmFa&rXUDD3s6`{bS=#SQH*fF_P~==1rs2OLF$i0 zq7>%vQ(Vm-)ni4X=tfiS=)8B3Fy=dvB}Q|DLRu@9Tsk7WNR_O5q}*>zj*C2pHelpH z(x@VoWn$jf%LY0%YFJT7}Q(}w^u zPxq=XP`QU@nSQti96IWC*9$<0?BuOYy%L2Bj^X{l!Q&}B*==J0d-cd?1+aMd-xM{# zctQ4E@r59TxPf+yci@ADi$`%I93{C@8T3@pRhG|Toe7;EE~s`O#&j2Dl97fjD5{uco5;lNL=2V%CU{P)ZnhgN#scr(m^ z4G)vrEWjAAo}+7mRvb#2262n6bk1IjjlEc*9Cg5?sGIf#GqCzKm|>LeFhcg*3HN!S zM8}UT4NJV$14jbO%+Ge!dZ($q5`=xA%;I!k(g6Yoa&~pM!I5^|? z1u6T#OePaP z5uM^UP}XqUF0H}+ezJw{$P@sTa78-4Hh+Oei*_@pFM^3+*Kt|OfgXYS!G@Xv09ufI zP<=S442)3<0h{V|1Mf{=KbXp>lf$?MJ(N2gEEHbsR56N|$x7FpKf$U5Y>y*YD6~64 z*pvzD^?tIM;WIBrfD46s4fJ2Kfb#X74xyT3W*=-KP^^izFfSt#xZ+x7<(J0`&~7g# z-vWV>WR>cFoTLzdz z)OjhDxxT&Dg=&u6VULFD;s-Yo>F0qPU)F;bpNFnQ9NNMbHTx@i?yaroS#T1@kcr!d z3^z8x5t3I1m0!LN>r>E7!mv}2Jt8m|ElPN^^xe=v3kQ!)VuKTTclbli>+gXvWF1wb z30_zmj`Fqrr7;9>PClm|$bjP1+T{G_2f%C{{)H+kj0W&-CteXgtgt3Ly=?$ze|?8( z7zUW{Z1GE+UAmxG-a-U%2z1I29|WWZ6}3XI^piNs9C$%^hkfCXu!zv0f?2|&PMp{* z<#|w=vFJnl!56_Gi_+~`5We)mEoA}ER1Ap7rJ#dv|hT|rxqb*u*INaLwKIUkOnh(+Z zOxm*PhRW-FQz#DoBJ>T_xx(R+b70Y?ffw86s;qa(}S1G%hCR1d1)BwcafY+FTG zk}`W{W^;*I#hx|0W^ax_NeUA4V9k;swGL*o&Dk8j6R2|_SZaLD7hinw#TQ@v-{QZP WwD@H(bH7^v00004B1U=M9T&4_wZz;_A}eQlQY3G(D*$r*MDRb(Ln(+`{zG z!nW{|p~C%9uG(~NksdK(Z_7I_(L#Ewa1l>ZN>}!JJ+^q?9PB2aRX6}7>mWw9e{3Z% zlXzy7HKL{arVQs0QdhdKY2>?6r`ojQUp(Tb$Hu0e zVDHeHTF|~g#WoNuUoiFL>v0JMiYGa<%Xi}>NGS)pi#73B*nPvRQdXb z?fE}s--N~YpO$UcCDWIUKN%gL$&h_ytJZOvhE5O!DE%_O{x3^Yqu$cW`K633V8VF={oI3t>;bX00t>o z;3McrSLsGtQFG#z9x0P^JK8#@G&`(nabh1JeN2U3x=pgGMxOs>yuvNu!?%7jC}_p| zvZ_poI;B*YO>^jQ(R^C#&+;>Ijs~SDQP=ebeX*Q*bmDE>AQx)4$<7gV?Lo#=iu~ms z*%i(N4r(bxTs|4_r8bR)l%&Hp1qKkmH@81kp)lafV!S<<0{CN8`M%5~FQH$aKeg)% zI&BUH&sRyviT#Ux4B022rt5m3^a-x7)3_!;3fng-cKJTgE7uQ+5iluCvJ-@98hfg$ zLML~v*2gqW1JZsf!$h>${Wc^w#l~yPIjMBZmhJm#fE)Xuxb!}CU|ws29riXZ zG5y{rXT@>L<}yCmR*YG{Oo)*aGYZ(0*gLRfYWa$4hyN}F>y6zZM;=5?W^~%%r(i-k zKEX%YA5KhEC>hn>TWgj{1F)9TmQF>IB8A)G@c?4kaeHsH86-3-%oS}aXEV?p4u1d? z#Eth=5qjMeD_}?SOPNZ$A4xu>%DL*jk={y-F_%+l%<1qnYOn@APJMm}1N5Fn^8h3N zSdJ+lO6AnKC3MlhKZS-S`Qy9$cknB5OIV^VWRsl}W{dpvVk88}6|f;Lp)zg)k1k=( zC!vho>4`d#r1w`hkrMV@D>M><7%>SqBmseQ5+vVMLxf(juT9WM=pvL5i{$>4e1XyK z0(M2M_codsjf7CvERvTZpbMgK0?jSy&v^>H6r}JgoJs!H25)FVusrXiX!$?1?D`UX zBEzrVA2WNhgc96@Qwaj*Ec$iXbPkYZ&=1($UXp>N0O#XKFV{{$%h@ZE=lCH@%et+q;nQfot13F-1=#|LCnYt0qIJ!Ajpz zL!ppB_xde943X39fpx>=(fI@Yn?#V|<`GT&zDaole`^XkqOm`v&KTjnM`VZ__d=eA z(3{1(SySQ~ni#HtlotnLeC~*YRDms|cumG2S%F(c6X6fn`aT+z;`0bh=~`ixZPFsz z2&xH@p>v=32t$Y$`G8&i5bDA2XOH8F^a6DCJ`+FILOz9^yBSWtq!X9L3)$;GDZ<=2 zM6ulB)(G4-VHaGR108w^yJSuLoYMc?1-p)7RBcaW-lqe%VTeDe3>l<5+&Y1dEACQ} zxw{Tr2R;(lG}s`;%_O*jC;S0^AhKE$atzujd!NGXFGy?RVu(yu07m~d?@*|`0o$ig zwIgSt@qB<%@+pE&FgHHtVZfB8E9+rtJ0v93;zpC3DD}+>T>1fgnc1^OD4CFRR3PMw zCLj~_mtdUH1P$ESW9RBV0aB!=i^8;rIad?hN(z(ERz&8UL>1DC@u^I+{^GyIZE6q+ zcR~dW&}OJgcin!L5qACUOIQjWq6H?&flmJg{htVX$9)BeYA$f%U}`(ol&PMq5-P8g zLf|%G;yhgmZQ?Aegs;OC9yu8Upx}yg=u;DzC6t~XB2$(J6AsL3{U;#33Jj4j#V=9H zg9faM(zP9Y_0f*yhwTUbR|Hkh{d4R1f)bzgf)FfVN?z{={fUWwrc>FUme&c&d?yB#~)KtZG!tp`4xj}{a6}A%YMTk>e52;(bY%O5E}zAW%j1zJR!}g@IWl1M|d*BNu@i#qePw}v!VqPEJYQx-b$r#B%qp4+HZ|bAZ zwzP|UE>v71MwG1uMAJHutv9b;)+?Vw!oI(L29f~kzsq{-F?(6;4S2%Q zgsk9BK4@#4f=5ECeP0xN3mzypQAs9TF};Y!RU(4D1$Uarkq%3?yusNaY8DZbRU1I% z11_!~7N^ffn!4BH&QAk`*%D%d7aQKl^Q0TUX9Nzyfvtsviupaa(TGhQ9g)4| zPN$O0EQ`OLQ82+kZDL$pCrv2qtRdM0xf5l-T-f6AtDY#M&w+hs`WIXLk^qxkH7GfJ`kmnEVVd(h3*cKwx-r*#??%3L<%6Z>Y= z=+HBfBB~(-H5-G^LrO2BgEF)yVVhBRk>Zbcz}7^19?b*p=Te-TqY`=RLyt8C{4$#g~652&vb2bQFZPgkdFW1w6;if+ph?Jsw|1=8|8* z=*HM%^{}MX<_6H9M->fVvMNt%fCTNn$<1<}VcOZBo71;FC>7%!a1k#=$IJd){lR@{ zh|)7n@*M?}6zY|RzKHR)gyQvmoZA83#E%+uQhOtlR8ky-P|7jJFnf)ihquI z)5#2VNWx0Ii2d-@e7Zb$z|>u*;yqfxTErvN`vdW{-=-;PIgjGHU4~NtY92yVW&l3o z23-~p!CT075AU~fMkcAde%spit!DNLEEait{qVK77P3*GB>XH1{dUdFNYgZKR^fp;fvMlGtJ2~z2T zYDkOFzW4#evo0=8p1<{@l;e^h;?+!O(m=$KB@-dXRGCtBmu7+To+DO9^@M+=w3Aa& z`C0e_z8Y#t$msI~U~GQbTi&8P+c+fNTK7x6;KUjLf<~5qCKl|R3`&+1gL%aK4dll^ zQF;Y_EF(e>g*G*NAw;3{u7~2Y{fCGHd_e2;C=V^FCej3N}rdOL<>3H{P~C|#Idb!Bq8Kz z-Q>v=3_6=-uo8QSPY$dvPW@cvBB#X8-2Yfg80`UAQKI_V(KTp2y0FE1o)RD|(0%W# zczv6u#4Cyb)Dm4CE&RUw&8&dtI3zWoUPgrCnE+AZWmk|jL(;U&E0F?zEpUu}VEm*d zyt6+5A4+qCt*^zT_sszMncTciV1I-5ooviKfGh;y1J$XbnEO5lK$)ml&3&H( zKpwpM#CGo6970a>6vds+CJsFa1Lf{q(J7qW})G9c<7RJ@1;jZ~;xl@O{5sp&h)< z)B8b?#n$#;AaPwRJoU@8K|U6WSukAhpb&xMOe z*Nc|#n1Y2sl|r&xVuYF&8Ua78x$klS{eyI2ys?V{au_m%1*iLvp~*RTYXK?FfZ{tT zWH}H=skgig3N{FdYP&b~^#1T#c=yErBMl$jxTG$Y7`9~x^$_j Date: Wed, 20 Sep 2017 17:52:02 +1200 Subject: [PATCH 06/32] Adding icon files to git #story[1245] --- src/main/java/seng302/gameServer/GameState.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 1dd9963a..0badbe59 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -38,8 +38,6 @@ public class GameState implements Runnable { static final int PREPATORY_TIME = 5 * -1000; private static final int TIME_TILL_START = 10 * 1000; - private static final Long POWERUP_TIMEOUT_MS = 10_000L; - private static final Integer STATE_UPDATES_PER_SECOND = 60; private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further private static final Double MARK_COLLISION_DISTANCE = 15d; @@ -327,7 +325,8 @@ public class GameState implements Runnable { private void checkPowerUpTimeout(ServerYacht yacht) { if (yacht.getPowerUp() != null) { - if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > POWERUP_TIMEOUT_MS) { + if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() + .getTimeout()) { yacht.powerDown(); sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired"); logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); From 061e49bab96728ad2b84f91cc230ddddcff0da63 Mon Sep 17 00:00:00 2001 From: William Muir Date: Fri, 22 Sep 2017 23:44:03 +1200 Subject: [PATCH 07/32] 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] --- .../java/seng302/gameServer/GameState.java | 172 +++++++++++++----- .../seng302/gameServer/MessageFactory.java | 5 + src/main/java/seng302/model/ServerYacht.java | 24 ++- src/main/java/seng302/model/token/Token.java | 2 - .../gameServer/server/ChatCommandsTest.java | 6 +- 5 files changed, 158 insertions(+), 51 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 3afd76ff..8f772653 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -46,12 +46,14 @@ public class GameState implements Runnable { private static final Double BOUNCE_DISTANCE_MARK = 20.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0; private static final Double COLLISION_VELOCITY_PENALTY = 0.3; + private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; + private static final Integer HANDLING_BOOST_MULTIPLIER = 2; private static Long previousUpdateTime; public static Double windDirection; 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 playerHasLeftFlag; @@ -85,7 +87,7 @@ public class GameState implements Runnable { GameState.hostIpAddress = hostIpAddress; customizationFlag = false; playerHasLeftFlag = false; - speedMultiplier = 1.0; + serverSpeedMultiplier = 1.0; currentStage = GameStages.LOBBYING; isRaceStarted = false; //set this when game stage changes to prerace @@ -283,9 +285,11 @@ public class GameState implements Runnable { tokensInPlay.clear(); //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(); + logger.debug("Spawned token of type " + token.getTokenType()); + tokensInPlay.add(token); } @@ -308,10 +312,10 @@ public class GameState implements Runnable { } for (ServerYacht yacht : yachts.values()) { updateVelocity(yacht); - checkPowerUpTimeout(yacht); yacht.runAutoPilot(); yacht.updateLocation(timeInterval); checkCollision(yacht); + preformTokenUpdates(yacht); //This update must always be done lsat if (yacht.getBoatStatus() != BoatStatus.FINISHED) { checkForLegProgression(yacht); raceFinished = false; @@ -324,13 +328,77 @@ 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 (System.currentTimeMillis() - yacht.getPowerUpStartTime() > yacht.getPowerUp() - .getTimeout()) { - yacht.powerDown(); - sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + "'s power-up token expired"); - logger.debug("Yacht: " + yacht.getShortName() + " powered down!"); + 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() + .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!"); + } + } + + + /** + * 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 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 - * @return Token which was collided with - * @param serverYacht The yacht to check for collision with a token + * Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up + * in the appropriate way + * @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) { - Double distance = GeoUtility.getDistance(token, serverYacht.getLocation()); + Double distance = GeoUtility.getDistance(token, yacht.getLocation()); 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 ServerYacht collidedYacht = checkYachtCollision(serverYacht); Mark collidedMark = checkMarkCollision(serverYacht); - Token collidedToken = checkTokenPickUp(serverYacht); if (collidedYacht != null) { GeoPoint originalLocation = serverYacht.getLocation(); @@ -431,34 +532,14 @@ public class GameState implements Runnable { ); 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) { Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); - Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier; - if (yacht.getPowerUp() != null) { - if (yacht.getPowerUp().equals(TokenType.BOOST)) { - maxBoatSpeed *= VELOCITY_BOOST_MULTIPLIER; - } - } + Double maxBoatSpeed = + GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht.getSpeedMultiplier(); Double currentVelocity = yacht.getCurrentVelocity(); // TODO: 15/08/17 remove magic numbers from these equations. @@ -700,6 +781,7 @@ public class GameState implements Runnable { String name = new String(customizeData); playerYacht.setBoatName(name); } 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 green = customizeData[1] & 0xFF; int blue = customizeData[2] & 0xFF; @@ -799,7 +881,7 @@ public class GameState implements Runnable { switch (words[2].trim()) { case "/speed": try { - setSpeedMultiplier(Double.valueOf(words[3])); + setServerSpeedMultiplier(Double.valueOf(words[3])); sendServerMessage(chatterMessage.getMessage_type(), "Speed modifier set to x" + words[3]); } catch (Exception e) { @@ -866,11 +948,11 @@ public class GameState implements Runnable { currentStage = GameStages.FINISHED; } - public static void setSpeedMultiplier (double multiplier) { - speedMultiplier = multiplier; + public static void setServerSpeedMultiplier(double multiplier) { + serverSpeedMultiplier = multiplier; } - public static double getSpeedMultiplier () { - return speedMultiplier; + public static double getServerSpeedMultiplier() { + return serverSpeedMultiplier; } } diff --git a/src/main/java/seng302/gameServer/MessageFactory.java b/src/main/java/seng302/gameServer/MessageFactory.java index 2ef38d29..d883d06c 100644 --- a/src/main/java/seng302/gameServer/MessageFactory.java +++ b/src/main/java/seng302/gameServer/MessageFactory.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import seng302.gameServer.messages.BoatLocationMessage; import seng302.gameServer.messages.BoatSubMessage; +import seng302.gameServer.messages.ChatterMessage; import seng302.gameServer.messages.RaceStartNotificationType; import seng302.gameServer.messages.RaceStartStatusMessage; import seng302.gameServer.messages.RaceStatus; @@ -157,4 +158,8 @@ public class MessageFactory { } return new YachtEventCodeMessage(serverYacht.getSourceId(), yachtEventType); } + + public static ChatterMessage makeChatterMessage(Integer messageType, String message) { + return new ChatterMessage(messageType, "SERVER: " + message); + } } diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index e7d936ca..21508774 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -56,6 +56,8 @@ public class ServerYacht { //PowerUp private TokenType powerUp; private Long powerUpStartTime; + private Integer speedMultiplier; + private Integer handlingMultiplier; public ServerYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName, @@ -77,6 +79,8 @@ public class ServerYacht { this.legNumber = 0; this.boatColor = Colors.getColor(sourceId - 1); this.powerUp = null; + this.speedMultiplier = 1; + this.handlingMultiplier = 1; this.hasEnteredRoundingZone = false; this.hasPassedLine = false; @@ -114,6 +118,8 @@ public class ServerYacht { public void powerDown() { this.powerUp = null; + this.speedMultiplier = 1; + this.handlingMultiplier = 1; } public Long getPowerUpStartTime() { @@ -130,7 +136,7 @@ public class ServerYacht { * @param amount the amount by which to adjust the boat heading. */ public void adjustHeading(Double amount) { - Double newVal = heading + amount; + Double newVal = heading + amount * handlingMultiplier; lastHeading = heading; heading = (double) Math.floorMod(newVal.longValue(), 360L); } @@ -429,4 +435,20 @@ public class ServerYacht { public BoatMeshType getBoatType() { 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; + } } diff --git a/src/main/java/seng302/model/token/Token.java b/src/main/java/seng302/model/token/Token.java index fcdbab5e..e1a16bd3 100644 --- a/src/main/java/seng302/model/token/Token.java +++ b/src/main/java/seng302/model/token/Token.java @@ -39,6 +39,4 @@ public class Token extends GeoPoint { tokenTypeList.remove(TokenType.RANDOM); tokenType = tokenTypeList.get(random.nextInt(tokenTypeList.size())); } - - } diff --git a/src/test/java/seng302/gameServer/server/ChatCommandsTest.java b/src/test/java/seng302/gameServer/server/ChatCommandsTest.java index cb0bc930..5c67c649 100644 --- a/src/test/java/seng302/gameServer/server/ChatCommandsTest.java +++ b/src/test/java/seng302/gameServer/server/ChatCommandsTest.java @@ -110,7 +110,7 @@ public class ChatCommandsTest { } catch (InterruptedException ie) { ie.printStackTrace(); } - Assert.assertEquals(5.0, GameState.getSpeedMultiplier(), 0.00001); + Assert.assertEquals(5.0, GameState.getServerSpeedMultiplier(), 0.00001); mst.terminate(); try { Thread.sleep(200); @@ -150,7 +150,7 @@ public class ChatCommandsTest { ie.printStackTrace(); } mst.terminate(); - Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); + Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001); try { Thread.sleep(2000); } catch (InterruptedException ie) { @@ -194,7 +194,7 @@ public class ChatCommandsTest { } catch (InterruptedException ie) { ie.printStackTrace(); } - Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001); + Assert.assertEquals(1.0, GameState.getServerSpeedMultiplier(), 0.00001); mst.terminate(); host.setSocketToClose(); client.setSocketToClose(); From e61b6d50a13253368af6c2042975a6fd276dbdbd Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 11:33:01 +1200 Subject: [PATCH 08/32] Small refactor. Fixed tokens to spawn on the minute mark. Moved updates of wind and token timers into gamestate from mainserver thread. Now triggered upon GameState change to start #story[1293] --- .../java/seng302/gameServer/GameState.java | 88 +++++++++++++++++-- .../seng302/gameServer/MainServerThread.java | 62 ------------- 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 8f772653..59056da6 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -35,25 +35,35 @@ public class GameState implements Runnable { private static Logger logger = LoggerFactory.getLogger(GameState.class); + private static final Integer STATE_UPDATES_PER_SECOND = 60; + + //Scheduling constants static final int WARNING_TIME = 10 * -1000; static final int PREPATORY_TIME = 5 * -1000; private static final int TIME_TILL_START = 10 * 1000; - private static final Integer STATE_UPDATES_PER_SECOND = 60; - private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further + //Wind Constants + private static final int MAX_WIND_SPEED = 12000; + private static final int MIN_WIND_SPEED = 8000; + + //Rounding Constants + private static final Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further + + //Collision constants private static final Double MARK_COLLISION_DISTANCE = 15d; public static final Double YACHT_COLLISION_DISTANCE = 25.0; private static final Double BOUNCE_DISTANCE_MARK = 20.0; public static final Double BOUNCE_DISTANCE_YACHT = 30.0; private static final Double COLLISION_VELOCITY_PENALTY = 0.3; + //Powerup Constants private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; private static final Integer HANDLING_BOOST_MULTIPLIER = 2; private static Long previousUpdateTime; public static Double windDirection; private static Double windSpeed; - private static Double serverSpeedMultiplier = 1d; + private static Double serverSpeedMultiplier; private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat. private static Boolean playerHasLeftFlag; @@ -90,7 +100,6 @@ public class GameState implements Runnable { serverSpeedMultiplier = 1.0; currentStage = GameStages.LOBBYING; isRaceStarted = false; - //set this when game stage changes to prerace previousUpdateTime = System.currentTimeMillis(); markOrder = new MarkOrder(); //This could be instantiated at some point with a select map? newMessageListeners = new ArrayList<>(); @@ -242,7 +251,15 @@ public class GameState implements Runnable { } catch (InterruptedException e) { System.out.println("[GameState] interrupted exception"); } - if (currentStage == GameStages.PRE_RACE || currentStage == GameStages.RACING) { + if (currentStage == GameStages.PRE_RACE) { + update(); + if (System.currentTimeMillis() > startTime) { + startSpawningTokens(); + startUpdatingWind(); + GameState.setCurrentStage(GameStages.RACING); + } + } + if (currentStage == GameStages.RACING) { update(); } @@ -252,6 +269,61 @@ public class GameState implements Runnable { } } + /** + * Start spawning coins every 60s after the first minute + */ + private void startSpawningTokens() { + Timer timer = new Timer("Token Spawning Timer"); + timer.schedule(new TimerTask() { + @Override + public void run() { + spawnNewToken(); + notifyMessageListeners(MessageFactory.getRaceXML()); + } + }, 0, 60000); + } + + // TODO: 29/08/17 wmu16 - This sort of update should be in game state + private static void startUpdatingWind() { + Timer timer = new Timer("Wind Updating Timer"); + timer.schedule(new TimerTask() { + @Override + public void run() { + updateWind(); + } + }, 0, 500); + } + + + private static void updateWind() { + Integer direction = GameState.getWindDirection().intValue(); + Integer windSpeed = GameState.getWindSpeedMMS().intValue(); + + Random random = new Random(); + + if (Math.floorMod(random.nextInt(), 2) == 0) { + direction += random.nextInt(4); + windSpeed += random.nextInt(20) + 459; + } else { + direction -= random.nextInt(4); + windSpeed -= random.nextInt(20) + 459; + } + + direction = Math.floorMod(direction, 360); + + if (windSpeed > MAX_WIND_SPEED) { + windSpeed -= random.nextInt(500); + } + + if (windSpeed <= MIN_WIND_SPEED) { + windSpeed += random.nextInt(500); + } + + GameState.setWindSpeed(Double.valueOf(windSpeed)); + GameState.setWindDirection(direction.doubleValue()); + } + + public static void updateBoat(Integer sourceId, BoatAction actionType) { ServerYacht playerYacht = yachts.get(sourceId); switch (actionType) { @@ -285,7 +357,7 @@ public class GameState implements Runnable { tokensInPlay.clear(); //Get a random token location with random type - Token token = allTokens.get(random.nextInt(allTokens.size() - 1) + 1); + Token token = allTokens.get(random.nextInt(allTokens.size())); token.assignRandomType(); logger.debug("Spawned token of type " + token.getTokenType()); @@ -307,9 +379,7 @@ public class GameState implements Runnable { Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0; previousUpdateTime = System.currentTimeMillis(); - if (System.currentTimeMillis() > startTime) { - GameState.setCurrentStage(GameStages.RACING); - } + for (ServerYacht yacht : yachts.values()) { updateVelocity(yacht); yacht.runAutoPilot(); diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index b5d8aa25..b4876748 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -37,9 +37,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { private static final int PORT = 4942; private static final Integer CLIENT_UPDATES_PER_SECOND = 60; - private static final int MAX_WIND_SPEED = 12000; - private static final int MIN_WIND_SPEED = 8000; - private boolean terminated; private Thread thread; @@ -101,8 +98,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { GameState.addMessageEventListener(this::broadcastMessage); terminated = false; thread = new Thread(this, "MainServer"); - startUpdatingWind(); - startSpawningTokens(); thread.start(); } @@ -187,63 +182,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate { } } - private static void updateWind(){ - Integer direction = GameState.getWindDirection().intValue(); - Integer windSpeed = GameState.getWindSpeedMMS().intValue(); - - Random random = new Random(); - - if (Math.floorMod(random.nextInt(), 2) == 0){ - direction += random.nextInt(4); - windSpeed += random.nextInt(20) + 459; - } - else{ - direction -= random.nextInt(4); - windSpeed -= random.nextInt(20) + 459; - } - - direction = Math.floorMod(direction, 360); - - if (windSpeed > MAX_WIND_SPEED){ - windSpeed -= random.nextInt(500); - } - - if (windSpeed <= MIN_WIND_SPEED){ - windSpeed += random.nextInt(500); - } - - GameState.setWindSpeed(Double.valueOf(windSpeed)); - GameState.setWindDirection(direction.doubleValue()); - } - - - - - // TODO: 29/08/17 wmu16 - This sort of update should be in game state - private static void startUpdatingWind(){ - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - updateWind(); - } - }, 0, 500); - } - - /** - * Start spawning coins every 60s after the first minute - */ - private void startSpawningTokens() { - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - GameState.spawnNewToken(); - broadcastMessage(MessageFactory.getRaceXML()); - } - }, 10000, 60000); - } - /** * A client has tried to connect to the server * From ecb3d4ecbf0a349cdbfcf6ae5bbfd6267ab92334 Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 11:49:56 +1200 Subject: [PATCH 09/32] Removed sendServerMessage to be replaced with notifyMessageListeners. Minor structure move arounds #story[1293] --- .../java/seng302/gameServer/GameState.java | 38 +++++++++---------- .../gameServer/messages/ChatterMessage.java | 2 +- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 59056da6..cfba8af1 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -92,7 +92,6 @@ public class GameState implements Runnable { windSpeed = 10000d; yachts = new HashMap<>(); tokensInPlay = new ArrayList<>(); - players = new ArrayList<>(); GameState.hostIpAddress = hostIpAddress; customizationFlag = false; @@ -688,7 +687,10 @@ public class GameState implements Runnable { if (hasProgressed) { if (currentMarkSeqID != 0 && !markOrder.isLastMark(currentMarkSeqID)) { - sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed leg " + yacht.getLegNumber()); + + String logMessage = yacht.getBoatName() + " passed leg " + yacht.getLegNumber(); + notifyMessageListeners( + MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); } yacht.incrementLegNumber(); sendMarkRoundingMessage(yacht); @@ -724,7 +726,9 @@ public class GameState implements Runnable { if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { yacht.setClosestCurrentMark(mark1); yacht.setBoatStatus(BoatStatus.RACING); - sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed start line"); + String logMessage = yacht.getBoatName() + " passed start line"; + notifyMessageListeners( + MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); return true; } } @@ -828,7 +832,10 @@ public class GameState implements Runnable { if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { yacht.setClosestCurrentMark(mark1); yacht.setBoatStatus(BoatStatus.FINISHED); - sendServerMessage(yacht.getSourceId(), yacht.getBoatName() + " passed finish line"); + + String logMessage = yacht.getBoatName() + " passed finish line"; + notifyMessageListeners( + MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); return true; } } @@ -937,13 +944,6 @@ public class GameState implements Runnable { roundingMark.getSourceID())); } - - public static void sendServerMessage(Integer messageType, String message) { - notifyMessageListeners(new ChatterMessage( - messageType, "SERVER: " + message - )); - } - public static void processChatter(ChatterMessage chatterMessage, boolean isHost) { String chatterText = chatterMessage.getMessage(); String[] words = chatterText.split("\\s+"); @@ -951,17 +951,19 @@ public class GameState implements Runnable { switch (words[2].trim()) { case "/speed": try { - setServerSpeedMultiplier(Double.valueOf(words[3])); - sendServerMessage(chatterMessage.getMessage_type(), - "Speed modifier set to x" + words[3]); + serverSpeedMultiplier = Double.valueOf(words[3]); + String logMessage = "Speed modifier set to x" + words[3]; + notifyMessageListeners(MessageFactory + .makeChatterMessage(chatterMessage.getMessageType(), logMessage)); } catch (Exception e) { Logger logger = LoggerFactory.getLogger(GameState.class); logger.error("cannot parse >speed value"); } return; case "/finish": - sendServerMessage(chatterMessage.getMessage_type(), - "Game will now finish"); + String logMessage = "Game will now finish"; + notifyMessageListeners(MessageFactory + .makeChatterMessage(chatterMessage.getMessageType(), logMessage)); endRace(); return; } @@ -1018,10 +1020,6 @@ public class GameState implements Runnable { currentStage = GameStages.FINISHED; } - public static void setServerSpeedMultiplier(double multiplier) { - serverSpeedMultiplier = multiplier; - } - public static double getServerSpeedMultiplier() { return serverSpeedMultiplier; } diff --git a/src/main/java/seng302/gameServer/messages/ChatterMessage.java b/src/main/java/seng302/gameServer/messages/ChatterMessage.java index 266fca62..4a3aec39 100644 --- a/src/main/java/seng302/gameServer/messages/ChatterMessage.java +++ b/src/main/java/seng302/gameServer/messages/ChatterMessage.java @@ -40,7 +40,7 @@ public class ChatterMessage extends Message { return message; } - public int getMessage_type() { + public int getMessageType() { return message_type; } } From 8c7f9a878db16e5042ab4a8d234d8dbdbaf07956 Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 13:23:16 +1200 Subject: [PATCH 10/32] 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); } } From c47e5b145019010b5a29629aaaa61a4fa34bc89c Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 16:31:18 +1200 Subject: [PATCH 11/32] Boat now changes color when it is bumped for a time ClientYacht: Added ColorChangeListener from GameView3D to re paint the boat when the color attribute is changed #story[1293] --- .../java/seng302/gameServer/GameState.java | 11 +++--- src/main/java/seng302/model/ClientYacht.java | 24 +++++++++++-- .../java/seng302/visualiser/GameClient.java | 34 ++++++++++++++----- .../java/seng302/visualiser/GameView3D.java | 24 ++++++++++--- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 496c7cb9..150fd652 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -60,7 +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; + public static final Long BUMPER_DISABLE_TIME = 5_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -359,8 +359,8 @@ public class GameState implements Runnable { //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); -// token.assignRandomType(); - token.assignType(TokenType.BUMPER); + token.assignRandomType(); +// token.assignType(TokenType.BUMPER); logger.debug("Spawned token of type " + token.getTokenType()); @@ -386,8 +386,7 @@ 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. + preformTokenUpdates(yacht); //This update must be done before collision. Sorta hacky checkCollision(yacht); if (yacht.getBoatStatus() != BoatStatus.FINISHED) { checkForLegProgression(yacht); @@ -424,7 +423,7 @@ public class GameState implements Runnable { boatTempShutDown(collidedYacht); notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht)); notifyMessageListeners( - MessageFactory.makeStatusEffectMessage(yacht, powerUp)); + MessageFactory.makeStatusEffectMessage(collidedYacht, powerUp)); } break; } diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index f626db30..0b5449ae 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -39,17 +39,21 @@ public class ClientYacht extends Observable { void notifyRounding(ClientYacht yacht, int legNumber); } + @FunctionalInterface + public interface ColorChangeListener { + + void notifyColorChange(ClientYacht yacht); + } + //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); } @@ -82,6 +86,7 @@ public class ClientYacht extends Observable { private List markRoundingListeners = new ArrayList<>(); private List powerUpListeners = new ArrayList<>(); private List powerDownListeners = new ArrayList<>(); + private List colorChangeListeners = new ArrayList<>(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); @@ -224,6 +229,9 @@ public class ClientYacht extends Observable { this.position = position; } + /** + * Powers down the boat and notifies the raceViewController to display + */ public void powerDown() { this.powerUp = null; for (PowerDownListener listener : powerDownListeners) { @@ -231,6 +239,11 @@ public class ClientYacht extends Observable { } } + /** + * powers up the boat and notifies the raceViewController to display + * + * @param tokenType The type of token that this boat is being powered up with + */ public void setPowerUp(TokenType tokenType) { this.powerUp = tokenType; for (PowerUpListener listener : powerUpListeners) { @@ -290,6 +303,9 @@ public class ClientYacht extends Observable { public void setColour(Color colour) { this.colour = colour; + for (ColorChangeListener listener : colorChangeListeners) { + listener.notifyColorChange(this); + } } @@ -319,6 +335,10 @@ public class ClientYacht extends Observable { powerDownListeners.add(listener); } + public void addColorChangeListener(ColorChangeListener listener) { + colorChangeListeners.add(listener); + } + public void removeMarkRoundingListener(MarkRoundingListener listener) { markRoundingListeners.remove(listener); } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 9947719e..0e4b4a7a 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -10,6 +10,8 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; +import java.util.Timer; +import java.util.TimerTask; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -19,6 +21,7 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; import javafx.util.Pair; import seng302.gameServer.GameStages; import seng302.gameServer.GameState; @@ -413,12 +416,12 @@ public class GameClient { ClientYacht thisYacht = allBoatsMap.get(yachtEventData.getSubjectId().intValue()); if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) { - showCollisionAlert(yachtEventData); + showCollisionAlert(thisYacht); } 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 + showDisableAlert(thisYacht); } else { TokenType tokenType = null; if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { @@ -438,16 +441,31 @@ public class GameClient { } } + + /** + * Turns a disabled boat black until the bumper affect wears off + * + * @param yacht The yacht to show as disabled + */ + private void showDisableAlert(ClientYacht yacht) { + Color originalColor = yacht.getColour(); + yacht.setColour(Color.BLACK); + + Timer disableTimer = new Timer("Disable Timer"); + disableTimer.schedule(new TimerTask() { + @Override + public void run() { + yacht.setColour(originalColor); + } + }, GameState.BUMPER_DISABLE_TIME); + } + /** * Tells race view to show a collision animation. */ - private void showCollisionAlert(YachtEventData yachtEventData) { + private void showCollisionAlert(ClientYacht yacht) { Sounds.playCrashSound(); - raceState.storeCollision( - allBoatsMap.get( - yachtEventData.getSubjectId().intValue() - ) - ); + raceState.storeCollision(yacht); } // TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index 486f0717..3c662dfb 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -467,11 +467,8 @@ public class GameView3D { wakesGroup.getChildren().add(newBoat.getWake()); wakes.add(newBoat.getWake()); boatObjectGroup.getChildren().add(newBoat); - clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { - BoatObject bo = boatObjects.get(boat); - Point2D p2d = findScaledXY(lat, lon); - bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); - }); + clientYacht.addLocationListener(this::updateBoatLocation); + clientYacht.addColorChangeListener(this::updateBoatColor); } Platform.runLater(() -> { gameObjects.getChildren().addAll(wakes); @@ -483,6 +480,23 @@ public class GameView3D { return view; } + /** + * Updates the boatObjects color with that of the clientYachts object. Used in notification from + * a listener on this attribute in clientYacht to re paint the boat mesh + * + * @param clientYacht The yacht to update the colour for + */ + private void updateBoatColor(ClientYacht clientYacht) { + boatObjects.get(clientYacht).setFill(clientYacht.getColour()); + } + + private void updateBoatLocation(ClientYacht boat, Double lat, Double lon, Double heading, + Boolean sailIn, Double velocity) { + BoatObject bo = boatObjects.get(boat); + Point2D p2d = findScaledXY(lat, lon); + bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); + } + /** * Adds a border to the GameView and rescales to the size of the border, does not rescale if a * border already exists. Assumes the border is larger than the course. From 78259f8e3327d7fe08528c694ee9cbf819e58f0a Mon Sep 17 00:00:00 2001 From: William Muir Date: Sat, 23 Sep 2017 16:54:03 +1200 Subject: [PATCH 12/32] Boat now changes color when it is bumped for a time ClientYacht: Added ColorChangeListener from GameView3D to re paint the boat when the color attribute is changed #story[1293] --- .../java/seng302/visualiser/GameClient.java | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 0e4b4a7a..061b593a 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -422,7 +422,7 @@ public class GameClient { Sounds.playTokenPickupSound(); // TODO: 23/09/17 This should be power down sound } else if (yachtEventData.getEventId() == YachtEventType.BUMPER_CRASH.getCode()) { showDisableAlert(thisYacht); - } else { + } else { //Else all token pickup types TokenType tokenType = null; if (yachtEventData.getEventId() == YachtEventType.TOKEN_VELOCITY.getCode()) { tokenType = TokenType.BOOST; @@ -436,7 +436,7 @@ public class GameClient { tokenType = TokenType.WIND_WALKER; } - showTokenPickUp(tokenType); + Sounds.playTokenPickupSound(); thisYacht.setPowerUp(tokenType); } } @@ -468,21 +468,6 @@ public class GameClient { raceState.storeCollision(yacht); } - // TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user - private void showTokenPickUp(TokenType tokenType) { - Sounds.playTokenPickupSound(); - switch (tokenType) { - case BOOST: - break; - case HANDLING: - break; - case WIND_WALKER: - break; - case BUMPER: - break; - } - } - private void formatAndSendChatMessage(String rawChat) { if (rawChat.length() > 0) { socketThread.sendChatterMessage( From 29b97a194dca9385ac75be56db3e526a2dcf77f7 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 25 Sep 2017 11:26:44 +1300 Subject: [PATCH 13/32] Merged dev back on #story[1293] --- .../java/seng302/gameServer/GameState.java | 24 +++++---- src/main/java/seng302/model/ServerYacht.java | 54 +++++++++---------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 14550218..5943625d 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -433,7 +433,7 @@ public class GameState implements Runnable { * @param yacht The yacht to disable */ private void boatTempShutDown(ServerYacht yacht) { - yacht.setSpeedMultiplier(0); + yacht.setPowerUpSpeedMultiplier(0); Timer shutDownTimer = new Timer("Shutdown Timer"); shutDownTimer.schedule(new TimerTask() { @Override @@ -542,13 +542,13 @@ public class GameState implements Runnable { TokenType tokenType = collidedToken.getTokenType(); switch (tokenType) { case BOOST: - yacht.setSpeedMultiplier(VELOCITY_BOOST_MULTIPLIER); + yacht.setPowerUpSpeedMultiplier(VELOCITY_BOOST_MULTIPLIER); break; case BUMPER: // TODO: 22/09/17 wmu16 break; case HANDLING: - yacht.setHandlingMultiplier(HANDLING_BOOST_MULTIPLIER); + yacht.setPowerUpHandlingMultiplier(HANDLING_BOOST_MULTIPLIER); break; case WIND_WALKER: // TODO: 22/09/17 wmu16 @@ -634,23 +634,29 @@ public class GameState implements Runnable { Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); Double maxBoatSpeed = - GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht.getSpeedMultiplier() * yacht.getMaxSpeedMultiplier(); + GeoUtility.knotsToMMS(boatSpeedInKnots) * serverSpeedMultiplier * yacht + .getPowerUpSpeedMultiplier() * yacht.getBoatTypeSpeedMultiplier(); Double currentVelocity = yacht.getCurrentVelocity(); // TODO: 15/08/17 remove magic numbers from these equations. if (yacht.getSailIn()) { if (currentVelocity < maxBoatSpeed - 500) { - yacht.changeVelocity((maxBoatSpeed / 100) * yacht.getAccelerationMultiplier()); + yacht.changeVelocity( + (maxBoatSpeed / 100) * yacht.getBoatTypeAccelerationMultiplier()); } else if (currentVelocity > maxBoatSpeed + 500) { - yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); + yacht.changeVelocity( + (-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier()); } else { - yacht.setCurrentVelocity((maxBoatSpeed) * yacht.getAccelerationMultiplier()); + yacht + .setCurrentVelocity((maxBoatSpeed) * yacht.getBoatTypeAccelerationMultiplier()); } } else { if (currentVelocity > 3000) { - yacht.changeVelocity((-currentVelocity / 200) * yacht.getAccelerationMultiplier()); + yacht.changeVelocity( + (-currentVelocity / 200) * yacht.getBoatTypeAccelerationMultiplier()); } else if (currentVelocity > 100) { - yacht.changeVelocity((-currentVelocity / 50) * yacht.getAccelerationMultiplier()); + yacht.changeVelocity( + (-currentVelocity / 50) * yacht.getBoatTypeAccelerationMultiplier()); } else if (currentVelocity <= 100) { yacht.setCurrentVelocity(0d); } diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index d1f36c7c..ee692d63 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -11,10 +11,6 @@ import seng302.utilities.GeoUtility; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import java.util.HashMap; -import java.util.Objects; -import java.util.Observable; -import java.util.Observer; -import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) @@ -28,9 +24,9 @@ public class ServerYacht { //Boat info private BoatMeshType boatType; private Double turnStep = 5.0; - private Double maxSpeedMultiplier = 1.0; - private Double turnStepMultiplier = 1.0; - private Double accelerationMultiplier = 1.0; + private Double boatTypeSpeedMultiplier = 1.0; + private Double boatTypeTurnStepMultiplier = 1.0; + private Double boatTypeAccelerationMultiplier = 1.0; private Integer sourceId; private String hullID; //matches HullNum in the XML spec. private String shortName; @@ -60,8 +56,8 @@ public class ServerYacht { //PowerUp private TokenType powerUp; private Long powerUpStartTime; - private Integer speedMultiplier; - private Integer handlingMultiplier; + private Integer powerUpSpeedMultiplier; + private Integer powerUpHandlingMultiplier; public ServerYacht(BoatMeshType boatType, Integer sourceId, String hullID, String shortName, @@ -83,8 +79,8 @@ public class ServerYacht { this.legNumber = 0; this.boatColor = Colors.getColor(sourceId - 1); this.powerUp = null; - this.speedMultiplier = 1; - this.handlingMultiplier = 1; + this.powerUpSpeedMultiplier = 1; + this.powerUpHandlingMultiplier = 1; this.hasEnteredRoundingZone = false; this.hasPassedLine = false; @@ -122,8 +118,8 @@ public class ServerYacht { public void powerDown() { this.powerUp = null; - this.speedMultiplier = 1; - this.handlingMultiplier = 1; + this.powerUpSpeedMultiplier = 1; + this.powerUpHandlingMultiplier = 1; } public Long getPowerUpStartTime() { @@ -140,7 +136,7 @@ public class ServerYacht { * @param amount the amount by which to adjust the boat heading. */ public void adjustHeading(Double amount) { - Double newVal = heading + amount * handlingMultiplier * turnStepMultiplier; + Double newVal = heading + amount * powerUpHandlingMultiplier * boatTypeTurnStepMultiplier; lastHeading = heading; heading = (double) Math.floorMod(newVal.longValue(), 360L); } @@ -433,18 +429,18 @@ public class ServerYacht { } public void setBoatType(BoatMeshType boatType) { - this.accelerationMultiplier = boatType.accelerationMultiplier; - this.maxSpeedMultiplier = boatType.maxSpeedMultiplier; - this.turnStepMultiplier = boatType.turnStep; + this.boatTypeAccelerationMultiplier = boatType.accelerationMultiplier; + this.boatTypeSpeedMultiplier = boatType.maxSpeedMultiplier; + this.boatTypeTurnStepMultiplier = boatType.turnStep; this.boatType = boatType; } - public Double getMaxSpeedMultiplier() { - return maxSpeedMultiplier; + public Double getBoatTypeSpeedMultiplier() { + return boatTypeSpeedMultiplier; } - public Double getAccelerationMultiplier(){ - return accelerationMultiplier; + public Double getBoatTypeAccelerationMultiplier() { + return boatTypeAccelerationMultiplier; } @@ -452,19 +448,19 @@ public class ServerYacht { return boatType; } - public Integer getSpeedMultiplier() { - return speedMultiplier; + public Integer getPowerUpSpeedMultiplier() { + return powerUpSpeedMultiplier; } - public void setSpeedMultiplier(Integer speedMultiplier) { - this.speedMultiplier = speedMultiplier; + public void setPowerUpSpeedMultiplier(Integer powerUpSpeedMultiplier) { + this.powerUpSpeedMultiplier = powerUpSpeedMultiplier; } - public Integer getHandlingMultiplier() { - return handlingMultiplier; + public Integer getPowerUpHandlingMultiplier() { + return powerUpHandlingMultiplier; } - public void setHandlingMultiplier(Integer handlingMultiplier) { - this.handlingMultiplier = handlingMultiplier; + public void setPowerUpHandlingMultiplier(Integer powerUpHandlingMultiplier) { + this.powerUpHandlingMultiplier = powerUpHandlingMultiplier; } } From 0211f2df38384c61a5e6c3967c0e74fd25d0f290 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 25 Sep 2017 17:38:09 +1300 Subject: [PATCH 14/32] Merged dev back on #story[1293] --- .../java/seng302/gameServer/GameState.java | 2 + src/main/resources/views/RaceView.fxml | 553 +++++++++--------- 2 files changed, 277 insertions(+), 278 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index fe603783..a8a4c642 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import javafx.scene.paint.Color; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 03728bde..9a02f517 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -24,285 +24,282 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + \ No newline at end of file From 7a4cdbe0c9b0c47ec46be06391bcb57b2833e617 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 11:12:29 +1300 Subject: [PATCH 15/32] Fixed WindWalker #story[1293] --- .../java/seng302/gameServer/GameState.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index a8a4c642..a7026682 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -81,6 +81,7 @@ public class GameState implements Runnable { private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; private static final Integer HANDLING_BOOST_MULTIPLIER = 2; public static final Long BUMPER_DISABLE_TIME = 5_000L; + private static final Long TOKEN_SPAWN_TIME = 15_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -297,7 +298,7 @@ public class GameState implements Runnable { spawnNewToken(); notifyMessageListeners(MessageFactory.getRaceXML()); } - }, 0, 15_000); + }, 0, TOKEN_SPAWN_TIME); } // TODO: 29/08/17 wmu16 - This sort of update should be in game state @@ -382,7 +383,7 @@ public class GameState implements Runnable { //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); token.assignRandomType(); -// token.assignType(TokenType.BUMPER); + token.assignType(TokenType.WIND_WALKER); logger.debug("Spawned token of type " + token.getTokenType()); @@ -508,21 +509,7 @@ public class GameState implements Runnable { } 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); - } - } + windDirection = (double) Math.floorMod(Math.round(heading + optimalAngle), 360L); } From 98abe64f007469e07fe2158f14a67cf3375afe4e Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Tue, 26 Sep 2017 17:07:02 +1300 Subject: [PATCH 16/32] Added splash and loading screen --- src/main/java/seng302/App.java | 2 +- src/main/java/seng302/utilities/Sounds.java | 4 + .../java/seng302/visualiser/GameClient.java | 3 +- .../controllers/RaceViewController.java | 42 +- .../controllers/SplashScreenController.java | 58 +++ .../visualiser/controllers/ViewManager.java | 12 +- src/main/resources/css/SplashScreenView.css | 10 + src/main/resources/views/RaceView.fxml | 407 ++++++++---------- src/main/resources/views/SplashScreen.fxml | 32 ++ 9 files changed, 336 insertions(+), 234 deletions(-) create mode 100644 src/main/java/seng302/visualiser/controllers/SplashScreenController.java create mode 100644 src/main/resources/css/SplashScreenView.css create mode 100644 src/main/resources/views/SplashScreen.fxml diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index 3c654f77..2052bbc4 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -63,7 +63,7 @@ public class App extends Application { @Override public void start(Stage primaryStage) throws Exception { - ViewManager.getInstance().initialStartView(primaryStage); + ViewManager.getInstance().initialiseSplashScreen(primaryStage); } diff --git a/src/main/java/seng302/utilities/Sounds.java b/src/main/java/seng302/utilities/Sounds.java index f8257b1f..b7db0439 100644 --- a/src/main/java/seng302/utilities/Sounds.java +++ b/src/main/java/seng302/utilities/Sounds.java @@ -101,6 +101,10 @@ public class Sounds { musicPlayer.setCycleCount(MediaPlayer.INDEFINITE); musicPlayer.setVolume(0.3); musicPlayer.play(); + musicPlayer.setMute(musicMuted); + if (soundEffect != null) { + soundEffect.stop(); + } } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index ad3a37d6..8f7b2118 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -275,12 +275,13 @@ public class GameClient { ClientYacht player = allBoatsMap.get(socketThread.getClientId()); raceView.loadRace(allBoatsMap, courseData, raceState, player); - + raceView.showView(); raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> { if (isPressed) { formatAndSendChatMessage(raceView.readChatInput()); } }); + } } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 3d011a0d..99b092ca 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -27,12 +27,8 @@ import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Series; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.control.TextField; +import javafx.scene.control.*; +import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; @@ -68,6 +64,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private final int CHAT_LIMIT = 128; + @FXML + private AnchorPane loadingScreenPane; + @FXML + private ImageView loadingScreen; @FXML private Pane basePane; @FXML @@ -131,6 +131,24 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private FinishDialogController finishDialogController; public void initialize() { + contentStackPane.setVisible(false); + Image loadingImage = new Image("PP.png"); + loadingScreen.setImage(loadingImage); + //Centers the Image within the image view + double w = 0; + double h = 0; + double ratioX = loadingScreen.getFitWidth() / loadingImage.getWidth(); + double ratioY = loadingScreen.getFitHeight() / loadingImage.getHeight(); + double reduceRatio = 0; + if(ratioX >= ratioY) { + reduceRatio = ratioY; + } else { + reduceRatio = ratioX; + } + w = loadingImage.getWidth() * reduceRatio; + h = loadingImage.getHeight() * reduceRatio; + loadingScreen.setX((loadingScreen.getFitWidth() - w) / 2); + loadingScreen.setY((loadingScreen.getFitHeight() - h) / 2); Sounds.stopMusic(); Sounds.playRaceMusic(); @@ -202,6 +220,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel createFinishDialog(finishedBoats); } + public void showView(){ + loadingScreenPane.setVisible(false); + contentStackPane.setVisible(true); + + Platform.runLater(new Runnable() { + @Override + public void run() { + contentStackPane.requestFocus(); + } + }); + } + /** * Create finishScreenDialog and set up finishDialogController. */ diff --git a/src/main/java/seng302/visualiser/controllers/SplashScreenController.java b/src/main/java/seng302/visualiser/controllers/SplashScreenController.java new file mode 100644 index 00000000..a3df3b4b --- /dev/null +++ b/src/main/java/seng302/visualiser/controllers/SplashScreenController.java @@ -0,0 +1,58 @@ +package seng302.visualiser.controllers; + +import com.jfoenix.controls.JFXDecorator; +import com.jfoenix.controls.JFXSnackbar; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.SceneAntialiasing; +import javafx.scene.image.Image; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import seng302.gameServer.ServerAdvertiser; +import seng302.utilities.Sounds; +import seng302.visualiser.GameClient; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Created by Kusal on 26-Sep-17. + */ +public class SplashScreenController implements Initializable{ + + @FXML + private StackPane rootPane; + + @Override + public void initialize(URL location, ResourceBundle resources) { + new SplashScreen().start(); + } + + + class SplashScreen extends Thread { + public void run(){ + try { + Thread.sleep(1000); + Platform.runLater(new Runnable() { + @Override + public void run() { + try { + Stage stage = new Stage(); + ViewManager.getInstance().initialStartView(stage); + } catch (Exception e) { + e.printStackTrace(); + } + rootPane.getScene().getWindow().hide(); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/seng302/visualiser/controllers/ViewManager.java b/src/main/java/seng302/visualiser/controllers/ViewManager.java index 8f98824c..7e126884 100644 --- a/src/main/java/seng302/visualiser/controllers/ViewManager.java +++ b/src/main/java/seng302/visualiser/controllers/ViewManager.java @@ -21,6 +21,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.stage.Stage; +import javafx.stage.StageStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.gameServer.ServerAdvertiser; @@ -55,10 +56,18 @@ public class ViewManager { if (instance == null) { instance = new ViewManager(); } - return instance; } + public void initialiseSplashScreen(Stage stage) throws IOException { + this.stage = stage; + Parent root = FXMLLoader.load(getClass().getResource("/views/SplashScreen.fxml")); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.initStyle(StageStyle.UNDECORATED); + stage.show(); + } + /** * Initialize the start view in the given stage. */ @@ -66,7 +75,6 @@ public class ViewManager { this.stage = stage; Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml")); stage.setTitle("Party Parrots At Sea"); - JFXDecorator decorator = new JFXDecorator(stage, root, false, true, true); decorator.setCustomMaximize(true); decorator.applyCss(); diff --git a/src/main/resources/css/SplashScreenView.css b/src/main/resources/css/SplashScreenView.css new file mode 100644 index 00000000..a4d1d68d --- /dev/null +++ b/src/main/resources/css/SplashScreenView.css @@ -0,0 +1,10 @@ +#background { + -fx-background-color: #E7F1F8; +} + +#headText { + -fx-background-color: transparent; + -fx-font-size: 52px; + -fx-text-fill: -fx-pp-light-text-color; + -fx-effect: -fx-pp-dropshadow-headers; +} \ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 879b2f22..5c295725 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -1,5 +1,12 @@ + + + + + + + @@ -14,232 +21,184 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/src/main/resources/views/SplashScreen.fxml b/src/main/resources/views/SplashScreen.fxml new file mode 100644 index 00000000..31449b7e --- /dev/null +++ b/src/main/resources/views/SplashScreen.fxml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8ba44d7476a718a964a56d1ea577861333a29b34 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 17:19:45 +1300 Subject: [PATCH 17/32] Minor commit for testing #story[1293] --- .../java/seng302/gameServer/GameState.java | 4 +-- .../gameServer/ServerToClientThread.java | 32 ++----------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index a7026682..ec761931 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -382,8 +382,8 @@ public class GameState implements Runnable { //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); - token.assignRandomType(); - token.assignType(TokenType.WIND_WALKER); +// token.assignRandomType(); + token.assignType(TokenType.BUMPER); logger.debug("Spawned token of type " + token.getTokenType()); diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index eb5a9ad0..09923b57 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -78,7 +78,6 @@ public class ServerToClientThread implements Runnable { private List connectionListeners = new ArrayList<>(); private DisconnectListener disconnectListener; - private ServerYacht yacht; private Player player; public ServerToClientThread(Socket socket) { @@ -101,33 +100,12 @@ public class ServerToClientThread implements Runnable { } private void setUpPlayer(){ - BufferedReader fn; - String fName = ""; - BufferedReader ln; - String lName = ""; + String shortName = "p" + sourceId; + String longName = "player " + sourceId; - fn = new BufferedReader( - new InputStreamReader( - ServerToClientThread.class.getResourceAsStream( - "/server_config/CSV_Database_of_First_Names.csv" - ) - ) - ); - List all = fn.lines().collect(Collectors.toList()); - fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size())); - ln = new BufferedReader( - new InputStreamReader( - ServerToClientThread.class.getResourceAsStream( - "/server_config/CSV_Database_of_Last_Names.csv" - ) - ) - ); - all = ln.lines().collect(Collectors.toList()); - lName = all.get(ThreadLocalRandom.current().nextInt(0, all.size())); ServerYacht yacht = new ServerYacht( - BoatMeshType.DINGHY, sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ" - ); + BoatMeshType.DINGHY, sourceId, sourceId.toString(), shortName, longName, "NZ"); player = new Player(socket, yacht); GameState.addYacht(sourceId, yacht); @@ -318,10 +296,6 @@ public class ServerToClientThread implements Runnable { return socket; } - public ServerYacht getYacht() { - return yacht; - } - public void addConnectionListener(ConnectionListener listener) { connectionListeners.add(listener); } From 671efcaf080cb095d4c4d71f2e2d25c783ea0d82 Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Tue, 26 Sep 2017 17:25:57 +1300 Subject: [PATCH 18/32] Adding spinners to the loading screen. --- src/main/resources/views/RaceView.fxml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 5c295725..f42c5fa9 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -193,7 +193,8 @@ - + + From b5076bc9762d83a01d625158ce2a7392a2511360 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 18:57:15 +1300 Subject: [PATCH 19/32] Fixed wind walker and bumper Added a hack to bumper so that the collision distance is larger than the regular collision distance Wind walker now makes you go at you max speed rather than VMG speed #story[1293] --- .../java/seng302/gameServer/GameState.java | 29 +++++++------ src/main/java/seng302/model/PolarTable.java | 41 +++++++++++++++++-- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index ec761931..56f01fe1 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -382,8 +382,8 @@ public class GameState implements Runnable { //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); -// token.assignRandomType(); - token.assignType(TokenType.BUMPER); + token.assignRandomType(); +// token.assignType(TokenType.WIND_WALKER); logger.debug("Spawned token of type " + token.getTokenType()); @@ -440,8 +440,9 @@ public class GameState implements Runnable { windWalk(yacht); break; case BUMPER: - ServerYacht collidedYacht = checkYachtCollision(yacht); + ServerYacht collidedYacht = checkYachtCollision(yacht, true); if (collidedYacht != null) { + System.out.println("WE OUT HERE"); yacht.powerDown(); boatTempShutDown(collidedYacht); notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht)); @@ -497,17 +498,12 @@ public class GameState implements Runnable { /** * This function changes the wind to be at an angle that causes the yacht in question to be at - * VMG. + * its fastest velocity * * @param yacht The yacht to fix the wind for */ private void windWalk(ServerYacht yacht) { - HashMap upwindOptimal = PolarTable.getOptimalUpwindVMG(windSpeed); - Double optimalAngle = null; - for (Double windAngle : upwindOptimal.keySet()) { - optimalAngle = windAngle; - } - + Double optimalAngle = PolarTable.getOptimalAngle(); Double heading = yacht.getHeading(); windDirection = (double) Math.floorMod(Math.round(heading + optimalAngle), 360L); } @@ -597,7 +593,7 @@ public class GameState implements Runnable { */ public static void checkCollision(ServerYacht serverYacht) { //Yacht Collision - ServerYacht collidedYacht = checkYachtCollision(serverYacht); + ServerYacht collidedYacht = checkYachtCollision(serverYacht, false); Mark collidedMark = checkMarkCollision(serverYacht); if (collidedYacht != null) { @@ -946,15 +942,22 @@ public class GameState implements Runnable { * Collision detection which iterates through all the yachts and check if any yacht collided * with this yacht. Return collided yacht or null if no collision. * + * UPDATE: HACK!!! wmu16 - forBumperCollision is (the goddamn dirtiest) dirty flag to fix a + * weird bug where the bumper collision would not be registerd but the knock back collision would. + * In other words, only set the 'forBumperCollision' flag true if used for the bumper power up. + * * @return yacht to compare to all other yachts. */ - private static ServerYacht checkYachtCollision(ServerYacht yacht) { + private static ServerYacht checkYachtCollision(ServerYacht yacht, Boolean forBumperCollision) { + Double collisionDistance = + (forBumperCollision) ? YACHT_COLLISION_DISTANCE + 2.5 : YACHT_COLLISION_DISTANCE; for (ServerYacht otherYacht : GameState.getYachts().values()) { if (otherYacht != yacht) { Double distance = GeoUtility .getDistance(otherYacht.getLocation(), yacht.getLocation()); - if (distance < YACHT_COLLISION_DISTANCE) { + ; + if (distance < collisionDistance) { return otherYacht; } } diff --git a/src/main/java/seng302/model/PolarTable.java b/src/main/java/seng302/model/PolarTable.java index 9334cc54..6d91cd71 100644 --- a/src/main/java/seng302/model/PolarTable.java +++ b/src/main/java/seng302/model/PolarTable.java @@ -4,7 +4,9 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Collections; import java.util.HashMap; +import java.util.Map; /** * A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised @@ -17,6 +19,7 @@ public final class PolarTable { private static HashMap> polarTable; private static HashMap> upwindOptimal; private static HashMap> downwindOptimal; + private static Double optimalAngle; private static int upTwaIndex; private static int dnTwaIndex; @@ -33,11 +36,13 @@ public final class PolarTable { upwindOptimal = new HashMap<>(); downwindOptimal = new HashMap<>(); - String line; + String line = null; + String check; Boolean isHeaderLine = true; try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) { - while ((line = br.readLine()) != null) { + while ((check = br.readLine()) != null) { + line = check; String[] thisLine = line.split(","); //Initial line in file @@ -66,7 +71,10 @@ public final class PolarTable { upwindOptimal.put(thisWindSpeed, thisUpWindPolar); downwindOptimal.put(thisWindSpeed, thisDnWindPolar); } + + } + getMaxSpeedAngle(line); } catch (IOException e) { System.out.println("[PolarTable] IO exception"); @@ -74,6 +82,27 @@ public final class PolarTable { } + /** + * Passes the final line of the polar table and iterates over the speeds for each + * angle, velocity pair to find the angle that produces the highest velocity + * + * @param line The last line of the polar file + */ + private static void getMaxSpeedAngle(String line) { + String[] theLastLine = line.split(","); + Double maxWindVal = Double.parseDouble(theLastLine[0]); + Double optimalAngle = Double.parseDouble(theLastLine[1]); + Double maxSpeed = Double.parseDouble(theLastLine[2]); + for (Map.Entry entry : polarTable.get(maxWindVal).entrySet()) { + if (entry.getValue() > maxSpeed) { + maxSpeed = entry.getValue(); + optimalAngle = entry.getKey(); + } + } + PolarTable.optimalAngle = optimalAngle; + } + + /** * Parses the header line of a polar file @@ -85,14 +114,18 @@ public final class PolarTable { String thisItem = thisLine[i]; if (thisItem.toLowerCase().startsWith("uptwa")) { upTwaIndex = i; - } - else if (thisItem.toLowerCase().startsWith("dntwa")) { + } else if (thisItem.toLowerCase().startsWith("dntwa")) { dnTwaIndex = i; } } } + public static Double getOptimalAngle() { + return optimalAngle; + } + + /** * @return The entire polar table */ From 4b7dfe38c491c9d57e119a26fedc2a4e6b28eabd Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 19:43:32 +1300 Subject: [PATCH 20/32] Fixed Boats powering up and down correctly. Icons respond correctly #story[1293] --- .../java/seng302/gameServer/GameState.java | 18 ++---------------- src/main/java/seng302/model/ServerYacht.java | 18 ++++++++++++++++++ .../controllers/RaceViewController.java | 5 +++++ src/main/resources/meshes/turning_pickup.dae | 14 +++++++------- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 56f01fe1..afd3a4e7 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -78,8 +78,8 @@ public class GameState implements Runnable { private static final Double COLLISION_VELOCITY_PENALTY = 0.3; //Powerup Constants - private static final Integer VELOCITY_BOOST_MULTIPLIER = 2; - private static final Integer HANDLING_BOOST_MULTIPLIER = 2; + public static final Integer VELOCITY_BOOST_MULTIPLIER = 2; + public static final Integer HANDLING_BOOST_MULTIPLIER = 2; public static final Long BUMPER_DISABLE_TIME = 5_000L; private static final Long TOKEN_SPAWN_TIME = 15_000L; @@ -550,20 +550,6 @@ public class GameState implements Runnable { } TokenType tokenType = collidedToken.getTokenType(); - switch (tokenType) { - case BOOST: - yacht.setPowerUpSpeedMultiplier(VELOCITY_BOOST_MULTIPLIER); - break; - case BUMPER: - // TODO: 22/09/17 wmu16 - break; - case HANDLING: - yacht.setPowerUpHandlingMultiplier(HANDLING_BOOST_MULTIPLIER); - break; - case WIND_WALKER: - // TODO: 22/09/17 wmu16 - break; - } yacht.powerUp(tokenType); String logMessage = diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 8d77dbda..0b15cb08 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -112,11 +112,29 @@ public class ServerYacht { location = geoPoint; } + /** + * Powers up a yacht with a given yacht, only after powering it down first to avoid double power + * ups + * + * @param powerUp The given power up + */ public void powerUp(TokenType powerUp) { + powerDown(); + switch (powerUp) { + case BOOST: + powerUpSpeedMultiplier = GameState.VELOCITY_BOOST_MULTIPLIER; + break; + case HANDLING: + powerUpHandlingMultiplier = GameState.HANDLING_BOOST_MULTIPLIER; + break; + } this.powerUp = powerUp; powerUpStartTime = System.currentTimeMillis(); } + /** + * Powers down a yacht, returning its power multipliers back to 1 + */ public void powerDown() { this.powerUp = null; this.powerUpSpeedMultiplier = 1; diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 76de92d3..716bc9fd 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -55,6 +55,7 @@ import seng302.model.RaceState; import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; import seng302.model.stream.xml.parser.RaceXMLData; +import seng302.model.token.Token; import seng302.model.token.TokenType; import seng302.utilities.Sounds; import seng302.visualiser.GameView3D; @@ -307,6 +308,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel */ private void displayPowerUpIcon(ClientYacht yacht, TokenType tokenType) { if (yacht == player) { + if (iconToDisplay != null) { + iconToDisplay.setVisible(false); + } switch (tokenType) { case BOOST: @@ -349,6 +353,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel if (yacht == player) { blinkingTimer.cancel(); iconToDisplay.setVisible(false); + iconToDisplay = null; } } diff --git a/src/main/resources/meshes/turning_pickup.dae b/src/main/resources/meshes/turning_pickup.dae index 2c4305f5..24e9fa08 100644 --- a/src/main/resources/meshes/turning_pickup.dae +++ b/src/main/resources/meshes/turning_pickup.dae @@ -5,8 +5,8 @@ Blender User Blender 2.78.0 commit date:2016-09-26, commit time:12:42, hash:4bb1e22 - 2017-09-19T15:45:46 - 2017-09-19T15:45:46 + 2017-09-26T19:13:35 + 2017-09-26T19:13:35 Z_UP @@ -23,10 +23,10 @@ 0 0 0 1 - 0.004555753 0.0885511 0.003947978 1 + 0.01630632 0.52949 0.0134405 1 - 0.25 0.25 0.25 1 + 0.125 0.125 0.125 1 50 @@ -49,10 +49,10 @@ 0 0 0 1 - 0.64 0.1458963 0.001825521 1 + 0.64 0.5334254 0 1 - 0.25 0.25 0.25 1 + 0.125 0.125 0.125 1 50 @@ -87,7 +87,7 @@ - 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284253 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616991 0.7240421 0.1947362 0.6616954 0.4911195 0.356821 0.7946575 0.4089463 0.6284252 0.6616985 -0.1875942 0.5773453 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875942 -0.5773453 0.7946577 0.03853034 -0.7487788 0.6616991 0.4911194 -0.356821 0.7946576 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875942 0.5773453 -0.7946577 -0.0385304 0.7487788 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911194 0.356821 -0.7946576 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911195 -0.356821 -0.7946575 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487788 -0.6616991 0.1875942 -0.5773453 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150899 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150898 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 + 0.7002241 -0.2680317 -0.6616988 0.9049891 -0.2680316 -0.3303847 0.02474653 -0.9435215 -0.330386 -0.8896973 -0.3150947 -0.3303849 -0.5746018 0.7487837 -0.3303875 0.5345759 0.7778646 -0.3303867 0.4089462 -0.6284252 0.6616985 -0.4712997 -0.5831224 0.6616985 -0.7002241 0.2680317 0.6616988 0.03853034 0.7487788 0.6616992 0.7240421 0.1947362 0.6616954 0.4911194 0.356821 0.7946576 0.4089462 0.6284253 0.6616984 -0.1875943 0.5773454 0.7946577 -0.4712997 0.5831224 0.6616985 -0.6070605 0 0.7946557 -0.7002241 -0.2680318 0.6616988 -0.1875943 -0.5773454 0.7946577 0.03853034 -0.7487788 0.6616992 0.4911193 -0.356821 0.7946577 0.7240421 -0.1947363 0.6616954 0.8896973 0.3150946 0.3303849 0.7946556 0.5773479 0.1875951 0.5746018 0.7487836 0.3303875 -0.02474653 0.9435214 0.3303861 -0.3035309 0.9341714 0.1875976 -0.5345759 0.7778646 0.3303867 -0.9049891 0.2680316 0.3303846 -0.9822458 0 0.1875985 -0.9049891 -0.2680316 0.3303846 -0.5345759 -0.7778646 0.3303867 -0.3035309 -0.9341714 0.1875975 -0.02474653 -0.9435214 0.3303861 0.5746018 -0.7487836 0.3303875 0.7946556 -0.5773479 0.1875951 0.8896973 -0.3150946 0.3303849 0.3035309 0.9341714 -0.1875975 0.02474653 0.9435215 -0.330386 -0.7946556 0.5773479 -0.1875951 -0.8896973 0.3150945 -0.3303849 -0.7946556 -0.5773479 -0.1875951 -0.5746018 -0.7487836 -0.3303875 0.3035309 -0.9341714 -0.1875976 0.5345759 -0.7778645 -0.3303867 0.9822458 0 -0.1875985 0.9049891 0.2680316 -0.3303847 0.4712997 0.5831224 -0.6616986 0.1875943 0.5773454 -0.7946577 -0.0385304 0.7487789 -0.6616991 -0.4089462 0.6284252 -0.6616984 -0.4911193 0.356821 -0.7946577 -0.7240421 0.1947362 -0.6616954 -0.7240421 -0.1947362 -0.6616954 -0.4911194 -0.356821 -0.7946576 -0.4089462 -0.6284252 -0.6616984 0.7002241 0.2680318 -0.6616988 0.6070605 0 -0.7946556 -0.0385304 -0.7487789 -0.6616991 0.1875943 -0.5773454 -0.7946577 0.4712997 -0.5831224 -0.6616986 0.1023808 -0.3150898 -0.9435235 -0.2680341 -0.1947365 -0.9435229 -0.2680341 0.1947365 -0.9435229 0.1023808 0.3150898 -0.9435235 0.802609 -0.5831265 -0.1256273 -0.306569 -0.9435216 -0.1256289 -0.9920774 0 -0.1256284 -0.306569 0.9435216 -0.1256289 0.802609 0.5831265 -0.1256273 0.2680341 0.1947365 0.9435229 -0.1023808 0.3150898 0.9435235 -0.3313045 0 0.943524 -0.1023808 -0.3150899 0.9435235 0.2680341 -0.1947365 0.9435229 0.306569 0.9435216 0.1256289 -0.802609 0.5831265 0.1256274 -0.802609 -0.5831265 0.1256274 0.306569 -0.9435216 0.1256289 0.9920774 0 0.1256284 0.3313045 0 -0.943524 From 330ccd272d6ea129835fb3eca13be3e383c0a796 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 21:13:35 +1300 Subject: [PATCH 21/32] The random token now has a 50% chance of causing your boat to have a speed penalty #story[1293] --- .../java/seng302/gameServer/GameState.java | 71 ++++++----- .../gameServer/messages/YachtEventType.java | 2 +- src/main/java/seng302/model/ServerYacht.java | 10 +- .../java/seng302/visualiser/GameView3D.java | 46 +++---- .../controllers/RaceViewController.java | 5 +- src/main/resources/views/RaceView.fxml | 116 +++++++++--------- 6 files changed, 131 insertions(+), 119 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index afd3a4e7..dfb8dae4 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -78,10 +78,11 @@ public class GameState implements Runnable { private static final Double COLLISION_VELOCITY_PENALTY = 0.3; //Powerup Constants - public static final Integer VELOCITY_BOOST_MULTIPLIER = 2; + public static final Double VELOCITY_BOOST_MULTIPLIER = 2d; public static final Integer HANDLING_BOOST_MULTIPLIER = 2; + private static final Double BAD_RANDOM_SPEED_PENALTY = 0.3; public static final Long BUMPER_DISABLE_TIME = 5_000L; - private static final Long TOKEN_SPAWN_TIME = 15_000L; + private static final Long TOKEN_SPAWN_TIME = 30_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -383,7 +384,7 @@ public class GameState implements Runnable { //Get a random token location with random type Token token = allTokens.get(random.nextInt(allTokens.size())); token.assignRandomType(); -// token.assignType(TokenType.WIND_WALKER); +// token.assignType(TokenType.RANDOM); logger.debug("Spawned token of type " + token.getTokenType()); @@ -430,7 +431,12 @@ public class GameState implements Runnable { * @param yacht The yacht to perform token checks on */ private void preformTokenUpdates(ServerYacht yacht) { - checkTokenPickUp(yacht); + Token collidedToken = checkTokenPickUp(yacht); + if (collidedToken != null) { + tokensInPlay.remove(collidedToken); + powerUpYacht(yacht, collidedToken); + } + checkPowerUpTimeout(yacht); TokenType powerUp = yacht.getPowerUp(); @@ -442,7 +448,6 @@ public class GameState implements Runnable { case BUMPER: ServerYacht collidedYacht = checkYachtCollision(yacht, true); if (collidedYacht != null) { - System.out.println("WE OUT HERE"); yacht.powerDown(); boatTempShutDown(collidedYacht); notifyMessageListeners(MessageFactory.makePowerDownMessage(yacht)); @@ -450,10 +455,36 @@ public class GameState implements Runnable { MessageFactory.makeStatusEffectMessage(collidedYacht, powerUp)); } break; + case RANDOM: + yacht.setPowerUpSpeedMultiplier(BAD_RANDOM_SPEED_PENALTY); } } } + + /** + * Powers up a yacht with the given token type. + * + * @param yacht The yacht to be powered up + * @param collidedToken The token which this yacht collided with + */ + private void powerUpYacht(ServerYacht yacht, Token collidedToken) { + //The random token has a 50% chance of becoming another token else becoming a speed detriment! + if (collidedToken.getTokenType() == TokenType.RANDOM && new Random().nextBoolean()) { + collidedToken.realiseRandom(); + } + + yacht.powerUp(collidedToken.getTokenType()); + 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()); + } + // 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 :/ /** @@ -462,7 +493,7 @@ public class GameState implements Runnable { * @param yacht The yacht to disable */ private void boatTempShutDown(ServerYacht yacht) { - yacht.setPowerUpSpeedMultiplier(0); + yacht.setPowerUpSpeedMultiplier(0d); Timer shutDownTimer = new Timer("Shutdown Timer"); shutDownTimer.schedule(new TimerTask() { @Override @@ -533,36 +564,18 @@ public class GameState implements Runnable { * Checks all tokensInPlay to see if a yacht has picked one up. If so, the yacht is powered up * in the appropriate way * @param yacht The yacht to check for collision with a token + * + * @return The token collided with */ - private void checkTokenPickUp(ServerYacht yacht) { - Token collidedToken = null; + private Token checkTokenPickUp(ServerYacht yacht) { for (Token token : tokensInPlay) { Double distance = GeoUtility.getDistance(token, yacht.getLocation()); if (distance < YACHT_COLLISION_DISTANCE) { - collidedToken = token; + return token; } } - if (collidedToken != null) { - tokensInPlay.remove(collidedToken); - if (collidedToken.getTokenType() == TokenType.RANDOM) { - collidedToken.realiseRandom(); - } - - TokenType tokenType = collidedToken.getTokenType(); - 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()); - } + return null; } diff --git a/src/main/java/seng302/gameServer/messages/YachtEventType.java b/src/main/java/seng302/gameServer/messages/YachtEventType.java index facda84e..9fd5170b 100644 --- a/src/main/java/seng302/gameServer/messages/YachtEventType.java +++ b/src/main/java/seng302/gameServer/messages/YachtEventType.java @@ -1,7 +1,7 @@ package seng302.gameServer.messages; /** - * Created by wmu16 on 11/09/17. + * Enum for different event types for the yacht */ public enum YachtEventType { COLLISION(33), diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 0b15cb08..c092447d 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -55,7 +55,7 @@ public class ServerYacht { //PowerUp private TokenType powerUp; private Long powerUpStartTime; - private Integer powerUpSpeedMultiplier; + private Double powerUpSpeedMultiplier; private Integer powerUpHandlingMultiplier; //turning mode @@ -80,7 +80,7 @@ public class ServerYacht { this.legNumber = 0; this.boatColor = Colors.getColor(sourceId - 1); this.powerUp = null; - this.powerUpSpeedMultiplier = 1; + this.powerUpSpeedMultiplier = 1d; this.powerUpHandlingMultiplier = 1; this.hasEnteredRoundingZone = false; this.hasPassedLine = false; @@ -137,7 +137,7 @@ public class ServerYacht { */ public void powerDown() { this.powerUp = null; - this.powerUpSpeedMultiplier = 1; + this.powerUpSpeedMultiplier = 1d; this.powerUpHandlingMultiplier = 1; } @@ -480,11 +480,11 @@ public class ServerYacht { this.continuouslyTurning = continuouslyTurning; } - public Integer getPowerUpSpeedMultiplier() { + public Double getPowerUpSpeedMultiplier() { return powerUpSpeedMultiplier; } - public void setPowerUpSpeedMultiplier(Integer powerUpSpeedMultiplier) { + public void setPowerUpSpeedMultiplier(Double powerUpSpeedMultiplier) { this.powerUpSpeedMultiplier = powerUpSpeedMultiplier; } diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index 027e92ad..5978675c 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -417,28 +417,30 @@ public class GameView3D { public void cameraMovement(KeyEvent event) { GameKeyBind keyBinds = GameKeyBind.getInstance(); KeyAction keyPressed = keyBinds.getKeyAction(event.getCode()); - switch (keyPressed) { - case ZOOM_IN: - ((RaceCamera) view.getCamera()).zoomIn(); - break; - case ZOOM_OUT: - ((RaceCamera) view.getCamera()).zoomOut(); - break; - case FORWARD: - ((RaceCamera) view.getCamera()).panUp(); - break; - case BACKWARD: - ((RaceCamera) view.getCamera()).panDown(); - break; - case LEFT: - ((RaceCamera) view.getCamera()).panLeft(); - break; - case RIGHT: - ((RaceCamera) view.getCamera()).panRight(); - break; - case VIEW: - toggleCamera(); - break; + if (keyPressed != null) { + switch (keyPressed) { + case ZOOM_IN: + ((RaceCamera) view.getCamera()).zoomIn(); + break; + case ZOOM_OUT: + ((RaceCamera) view.getCamera()).zoomOut(); + break; + case FORWARD: + ((RaceCamera) view.getCamera()).panUp(); + break; + case BACKWARD: + ((RaceCamera) view.getCamera()).panDown(); + break; + case LEFT: + ((RaceCamera) view.getCamera()).panLeft(); + break; + case RIGHT: + ((RaceCamera) view.getCamera()).panRight(); + break; + case VIEW: + toggleCamera(); + break; + } } } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index 716bc9fd..7395d5df 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -121,7 +121,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel @FXML private Label positionLabel, boatSpeedLabel, boatHeadingLabel; @FXML - private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon; + private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon; //Race Data private Map participants; @@ -325,6 +325,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel case BUMPER: iconToDisplay = bumperIcon; break; + case RANDOM: + iconToDisplay = badRandomIcon; + break; default: iconToDisplay = velocityIcon; } diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 9a02f517..7e53d81b 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -25,9 +25,8 @@ @@ -48,17 +47,15 @@ valignment="BOTTOM" vgrow="SOMETIMES"/> - + - + - + @@ -90,25 +87,20 @@ - - + + - + - + - + - + @@ -131,18 +123,16 @@ minWidth="90.0" prefWidth="90.0"/> - + - + - + - + - + - \ No newline at end of file + From 5248921576e6823ea55b23877d4b7c0f3bf1a0fe Mon Sep 17 00:00:00 2001 From: Zhi You Tan Date: Tue, 26 Sep 2017 22:52:58 +1300 Subject: [PATCH 22/32] Multiple fixes: - fix last button no longer default focused when loading keybinding dialog - fix click and mouse exit still focused on button - closing keybinding does not focus on chat anymore #story[1273] --- .../visualiser/controllers/ViewManager.java | 1 + .../dialogs/KeyBindingDialogController.java | 7 +- src/main/resources/views/RaceView.fxml | 85 ++++++++----------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/src/main/java/seng302/visualiser/controllers/ViewManager.java b/src/main/java/seng302/visualiser/controllers/ViewManager.java index 5e3d6748..2f843a4c 100644 --- a/src/main/java/seng302/visualiser/controllers/ViewManager.java +++ b/src/main/java/seng302/visualiser/controllers/ViewManager.java @@ -221,6 +221,7 @@ public class ViewManager { .getController(); keyBindingDialogController.setGameClient(this.gameClient); keyBindingDialog.show(); + decorator.requestFocus(); Sounds.playButtonClick(); } } diff --git a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java index c650bc59..1f638c5a 100644 --- a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java +++ b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java @@ -71,8 +71,7 @@ public class KeyBindingDialogController implements Initializable { buttons = new ArrayList<>(); Collections.addAll(buttons, zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn, - viewButton, - rightButton, leftButton, forwardButton, backwardButton); + viewButton, rightButton, leftButton, forwardButton, backwardButton); bindButtonWithAction(); loadKeyBind(); @@ -91,9 +90,6 @@ public class KeyBindingDialogController implements Initializable { }); closeLabel.setOnMouseClicked(event -> ViewManager.getInstance().closeKeyBindingDialog()); - - keyBindingDialogHeader.setFocusTraversable(true); - keyBindingDialogHeader.requestFocus(); } /** @@ -161,6 +157,7 @@ public class KeyBindingDialogController implements Initializable { + "-fx-background-color: -fx-pp-front-color; " + "-fx-text-fill: -fx-pp-theme-color; " + "-fx-font-size: 13;"); + keyBindingDialogHeader.requestFocus(); } /** diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 879b2f22..4ee22b65 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -7,17 +7,16 @@ - + @@ -38,17 +37,15 @@ valignment="BOTTOM" vgrow="SOMETIMES"/> - + - + - + @@ -80,25 +77,20 @@ - - + + - + - + - + - + @@ -121,22 +113,21 @@ minWidth="90.0" prefWidth="90.0"/> - + - - + - + @@ -158,29 +149,27 @@ prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="BOTTOM"> - - + - + - + - - + + From 6d51ea35745d465db2ccea5a24d2d3422fcfa1a1 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 01:44:01 +1300 Subject: [PATCH 24/32] Created a random place token generator. Generates a location between any leg of the race in a random angle / distance of the radius of the centre point of the two gates to one of the gates #story[1293] --- .../java/seng302/gameServer/GameState.java | 47 +++----------- .../java/seng302/model/mark/MarkOrder.java | 14 ++-- src/main/java/seng302/model/token/Token.java | 13 ++++ .../java/seng302/utilities/RandomSpawn.java | 64 +++++++++++++++++++ 4 files changed, 97 insertions(+), 41 deletions(-) create mode 100644 src/main/java/seng302/utilities/RandomSpawn.java diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index dfb8dae4..5236a14e 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; @@ -38,6 +39,7 @@ import seng302.model.mark.MarkOrder; import seng302.model.token.Token; import seng302.model.token.TokenType; import seng302.utilities.GeoUtility; +import seng302.utilities.RandomSpawn; import seng302.utilities.XMLParser; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; @@ -82,7 +84,7 @@ public class GameState implements Runnable { public static final Integer HANDLING_BOOST_MULTIPLIER = 2; private static final Double BAD_RANDOM_SPEED_PENALTY = 0.3; public static final Long BUMPER_DISABLE_TIME = 5_000L; - private static final Long TOKEN_SPAWN_TIME = 30_000L; + private static final Long TOKEN_SPAWN_TIME = 15_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -99,13 +101,12 @@ public class GameState implements Runnable { private static GameStages currentStage; private static MarkOrder markOrder; private static long startTime; - private static Set marks; + private static List marks; private static List courseLimit; private static Integer maxPlayers = 8; - - private static List allTokens; private static List tokensInPlay; + private static RandomSpawn randomSpawn; private static List newMessageListeners; @@ -126,14 +127,12 @@ public class GameState implements Runnable { previousUpdateTime = System.currentTimeMillis(); markOrder = new MarkOrder(); //This could be instantiated at some point with a select map? newMessageListeners = new ArrayList<>(); - allTokens = makeTokens(); + marks = new MarkOrder().getAllMarks(); + randomSpawn = new RandomSpawn(markOrder.getOrderedUniqueCompoundMarks()); resetStartTime(); - - new Thread(this, "GameState").start(); //Run the auto updates on the game state - - marks = new MarkOrder().getAllMarks(); setCourseLimit("/server_config/race.xml"); + new Thread(this, "GameState").start(); //Run the auto updates on the game state } private void setCourseLimit(String url) { @@ -151,29 +150,10 @@ public class GameState implements Runnable { courseLimit = XMLParser.parseRace(document).getCourseLimit(); } - - /** - * Make a pre defined set of tokensInPlay. //TODO wmu16 - Should read from some file for each - * race ideally - * - * @return A list of possible tokensInPlay for this race - */ - private ArrayList makeTokens() { - Token token1 = new Token(TokenType.BOOST, 57.66946, 11.83154); - Token token2 = new Token(TokenType.BOOST, 57.66877, 11.83382); - Token token3 = new Token(TokenType.BOOST, 57.66914, 11.83965); - Token token4 = new Token(TokenType.BOOST, 57.66684, 11.83214); - return new ArrayList<>(Arrays.asList(token1, token2, token3, token4)); - } - public static String getHostIpAddress() { return hostIpAddress; } - public static Set getMarks() { - return Collections.unmodifiableSet(marks); - } - public static List getPlayers() { return players; } @@ -378,16 +358,9 @@ public class GameState implements Runnable { * Broadasts a new race status message to show this update */ 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.assignType(TokenType.RANDOM); - + Token token = randomSpawn.getRandomTokenLocation(); logger.debug("Spawned token of type " + token.getTokenType()); - tokensInPlay.add(token); } @@ -912,7 +885,7 @@ public class GameState implements Runnable { } private static Mark checkMarkCollision(ServerYacht yacht) { - Set marksInRace = GameState.getMarks(); + Set marksInRace = new HashSet<>(marks); for (Mark mark : marksInRace) { if (GeoUtility.getDistance(yacht.getLocation(), mark) <= MARK_COLLISION_DISTANCE) { diff --git a/src/main/java/seng302/model/mark/MarkOrder.java b/src/main/java/seng302/model/mark/MarkOrder.java index 0e250e99..7f296aee 100644 --- a/src/main/java/seng302/model/mark/MarkOrder.java +++ b/src/main/java/seng302/model/mark/MarkOrder.java @@ -24,8 +24,9 @@ import java.util.*; */ public class MarkOrder { private List raceMarkOrder; + private List orderedUniqueCompoundMarks; private Logger logger = LoggerFactory.getLogger(MarkOrder.class); - private Set allMarks; + private List allMarks; public MarkOrder(){ loadRaceProperties(); @@ -44,6 +45,10 @@ public class MarkOrder { return Collections.unmodifiableList(raceMarkOrder); } + public List getOrderedUniqueCompoundMarks() { + return orderedUniqueCompoundMarks; + } + /** * @param seqID The seqID of the current mark the boat is heading to * @return A Boolean indicating if this coming mark is the last one (finish line) @@ -75,8 +80,8 @@ public class MarkOrder { return raceMarkOrder.get(currentSeqID + 1); } - public Set getAllMarks(){ - return Collections.unmodifiableSet(allMarks); + public List getAllMarks() { + return Collections.unmodifiableList(allMarks); } /** @@ -89,7 +94,7 @@ public class MarkOrder { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document doc; - allMarks = new HashSet<>(); + allMarks = new ArrayList<>(); try { db = dbf.newDocumentBuilder(); @@ -105,6 +110,7 @@ public class MarkOrder { logger.debug("Loaded RaceXML for mark order"); List corners = data.getMarkSequence(); Map marks = data.getCompoundMarks(); + orderedUniqueCompoundMarks = new ArrayList<>(marks.values()); List course = new ArrayList<>(); for (Corner corner : corners){ CompoundMark compoundMark = marks.get(corner.getCompoundMarkID()); diff --git a/src/main/java/seng302/model/token/Token.java b/src/main/java/seng302/model/token/Token.java index ed8b87eb..0e6e86b8 100644 --- a/src/main/java/seng302/model/token/Token.java +++ b/src/main/java/seng302/model/token/Token.java @@ -15,11 +15,24 @@ public class Token extends GeoPoint { private TokenType tokenType; private Random random = new Random(); + //Constructor for creating a specific type client side public Token(TokenType tokenType, double lat, double lng) { super(lat, lng); this.tokenType = tokenType; } + //Making random type server side + public Token(double lat, double lng) { + super(lat, lng); + assignRandomType(); + } + + //Making random type server side + public Token(GeoPoint geoPoint) { + super(geoPoint.getLat(), geoPoint.getLng()); + assignRandomType(); + } + public TokenType getTokenType() { return tokenType; } diff --git a/src/main/java/seng302/utilities/RandomSpawn.java b/src/main/java/seng302/utilities/RandomSpawn.java new file mode 100644 index 00000000..0a0f0702 --- /dev/null +++ b/src/main/java/seng302/utilities/RandomSpawn.java @@ -0,0 +1,64 @@ +package seng302.utilities; + +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import seng302.model.GeoPoint; +import seng302.model.mark.CompoundMark; +import seng302.model.token.Token; + +/** + * A class for generating and spawning tokens in random locations + * Created by wmu16 on 27/09/17. + */ +public class RandomSpawn { + + private static final Integer DEGREES_IN_CIRCLE = 360; + + private HashMap spawnRadii; + private Random random; + + /** + * @param markOrder this must be the ORDERED list of marks. Better yet UNIQUE to avoid over + * computation + */ + public RandomSpawn(List markOrder) { + this.spawnRadii = new HashMap<>(); + random = new Random(); + + spawnRadii = generateSpawnRadii(markOrder); + } + + private HashMap generateSpawnRadii(List markOrder) { + System.out.println(markOrder); + HashMap spawnRadii = new HashMap<>(); + for (int i = 0; i < markOrder.size() - 1; i++) { + GeoPoint spawnCentre = GeoUtility.getDirtyMidPoint( + markOrder.get(i).getMidPoint(), + markOrder.get(i + 1).getMidPoint()); + + Double distance = GeoUtility.getDistance(spawnCentre, markOrder.get(i).getMidPoint()); + spawnRadii.put(spawnCentre, distance); + } + + return spawnRadii; + } + + + /** + * @return A random token type at a random location in a random radii of the set of possible + * radii + */ + public Token getRandomTokenLocation() { + Object[] keys = spawnRadii.keySet().toArray(); + GeoPoint randomSpawnCentre = (GeoPoint) keys[random.nextInt(keys.length)]; + Double spawnRadius = spawnRadii.get(randomSpawnCentre); + Double randomDistance = spawnRadius * random.nextDouble(); + Double randomAngle = random.nextDouble() * DEGREES_IN_CIRCLE; + GeoPoint randomLocation = GeoUtility + .getGeoCoordinate(randomSpawnCentre, randomAngle, randomDistance); + return new Token(randomLocation); + + } + +} From 10fa51a10547481b0c7765374c5743b5334ee8f9 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 02:27:30 +1300 Subject: [PATCH 25/32] Added some testing for the Random spawner #story[1293] --- .../java/seng302/utilities/RandomSpawn.java | 1 - .../seng302/utilities/RandomSpawnTest.java | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/test/java/seng302/utilities/RandomSpawnTest.java diff --git a/src/main/java/seng302/utilities/RandomSpawn.java b/src/main/java/seng302/utilities/RandomSpawn.java index 0a0f0702..929e5766 100644 --- a/src/main/java/seng302/utilities/RandomSpawn.java +++ b/src/main/java/seng302/utilities/RandomSpawn.java @@ -30,7 +30,6 @@ public class RandomSpawn { } private HashMap generateSpawnRadii(List markOrder) { - System.out.println(markOrder); HashMap spawnRadii = new HashMap<>(); for (int i = 0; i < markOrder.size() - 1; i++) { GeoPoint spawnCentre = GeoUtility.getDirtyMidPoint( diff --git a/src/test/java/seng302/utilities/RandomSpawnTest.java b/src/test/java/seng302/utilities/RandomSpawnTest.java new file mode 100644 index 00000000..60966ab8 --- /dev/null +++ b/src/test/java/seng302/utilities/RandomSpawnTest.java @@ -0,0 +1,50 @@ +package seng302.utilities; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import seng302.model.GeoPoint; +import seng302.model.mark.CompoundMark; +import seng302.model.mark.Mark; +import seng302.model.token.Token; + +/** + * Created by wmu16 on 27/09/17. + */ +public class RandomSpawnTest { + + private RandomSpawn randomSpawn; + + Mark mark1 = new Mark("mark1", 0, 57.670333, 11.827833, 0); + Mark mark2 = new Mark("mark2", 1, 57.671829, 11.842049, 1); + CompoundMark compoundMark1 = new CompoundMark(0, "mark1", + new ArrayList<>(Arrays.asList(mark1))); + CompoundMark compoundMark2 = new CompoundMark(0, "mark1", + new ArrayList<>(Arrays.asList(mark2))); + + List markOrder = new ArrayList<>(Arrays.asList(compoundMark1, compoundMark2)); + + @Before + public void setup() { + randomSpawn = new RandomSpawn(markOrder); + } + + @Test + public void testGetRandomTokenLocation() { + GeoPoint testMidPoint = GeoUtility + .getDirtyMidPoint(compoundMark1.getMidPoint(), compoundMark2.getMidPoint()); + Double maxDistance = GeoUtility.getDistance(testMidPoint, compoundMark2.getMidPoint()); + for (int i = 0; i < 1000; i++) { + Token token = randomSpawn.getRandomTokenLocation(); + Double distanceFromCentreRadius = GeoUtility.getDistance(testMidPoint, token); + assertTrue("Out of bounds token", distanceFromCentreRadius <= maxDistance); + } + + + } + +} \ No newline at end of file From 658a342118c35b0a61b409d511edda5755ed5f05 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 03:01:23 +1300 Subject: [PATCH 26/32] Added the bad random to git sorree forgot to git add #story[1293] --- src/main/resources/icons/ayy_lmao.gif | Bin 0 -> 23971 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/icons/ayy_lmao.gif diff --git a/src/main/resources/icons/ayy_lmao.gif b/src/main/resources/icons/ayy_lmao.gif new file mode 100644 index 0000000000000000000000000000000000000000..884b1f5098b7b3acaa6e3a4e984bde490f32e7d9 GIT binary patch literal 23971 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 literal 0 HcmV?d00001 From 85ca91db964670edcd9efd70a88bf95a3dc48d1a Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 11:55:40 +1300 Subject: [PATCH 27/32] Minor fixes. Only one person can have wind walker now. Tokens spawn at 30s rather than 15 #story[1293] --- .../java/seng302/gameServer/GameState.java | 51 ++++++++++++------- .../java/seng302/model/token/TokenType.java | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 5236a14e..795d42dc 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -84,7 +84,7 @@ public class GameState implements Runnable { public static final Integer HANDLING_BOOST_MULTIPLIER = 2; private static final Double BAD_RANDOM_SPEED_PENALTY = 0.3; public static final Long BUMPER_DISABLE_TIME = 5_000L; - private static final Long TOKEN_SPAWN_TIME = 15_000L; + private static final Long TOKEN_SPAWN_TIME = 30_000L; private static Long previousUpdateTime; public static Double windDirection; @@ -360,6 +360,7 @@ public class GameState implements Runnable { private void spawnNewToken() { tokensInPlay.clear(); Token token = randomSpawn.getRandomTokenLocation(); + token.assignType(TokenType.WIND_WALKER); logger.debug("Spawned token of type " + token.getTokenType()); tokensInPlay.add(token); } @@ -436,26 +437,47 @@ public class GameState implements Runnable { /** - * Powers up a yacht with the given token type. + * Powers up a thisYacht with the given token type. * - * @param yacht The yacht to be powered up - * @param collidedToken The token which this yacht collided with + * @param thisYacht The yacht to be powered up + * @param collidedToken The token which this thisYacht collided with */ - private void powerUpYacht(ServerYacht yacht, Token collidedToken) { + private void powerUpYacht(ServerYacht thisYacht, Token collidedToken) { //The random token has a 50% chance of becoming another token else becoming a speed detriment! if (collidedToken.getTokenType() == TokenType.RANDOM && new Random().nextBoolean()) { collidedToken.realiseRandom(); } - yacht.powerUp(collidedToken.getTokenType()); + //If another yacht has the wind walker token, They should be powered down. Only one allowed! + else if (collidedToken.getTokenType() == TokenType.WIND_WALKER) { + for (ServerYacht otherYacht : yachts.values()) { + if (otherYacht != thisYacht && otherYacht.getPowerUp() == TokenType.WIND_WALKER) { + powerDownYacht(otherYacht); + } + } + } + + thisYacht.powerUp(collidedToken.getTokenType()); String logMessage = - yacht.getBoatName() + " has picked up a " + collidedToken.getTokenType().getName() + thisYacht.getBoatName() + " has picked up a " + collidedToken.getTokenType().getName() + " token"; - notifyMessageListeners(MessageFactory.makeChatterMessage(yacht.getSourceId(), logMessage)); + notifyMessageListeners( + MessageFactory.makeChatterMessage(thisYacht.getSourceId(), logMessage)); notifyMessageListeners(MessageFactory.getRaceXML()); - notifyMessageListeners(MessageFactory.makePickupMessage(yacht, collidedToken)); + notifyMessageListeners(MessageFactory.makePickupMessage(thisYacht, collidedToken)); logger.debug( - "Yacht: " + yacht.getShortName() + " got powerup " + collidedToken.getTokenType()); + "Yacht: " + thisYacht.getShortName() + " got powerup " + collidedToken.getTokenType()); + } + + private void powerDownYacht(ServerYacht yacht) { + 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(); } // 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 :/ @@ -487,14 +509,7 @@ public class GameState implements Runnable { 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(); + powerDownYacht(yacht); } } } diff --git a/src/main/java/seng302/model/token/TokenType.java b/src/main/java/seng302/model/token/TokenType.java index 8ae60dcc..0ce24d48 100644 --- a/src/main/java/seng302/model/token/TokenType.java +++ b/src/main/java/seng302/model/token/TokenType.java @@ -9,7 +9,7 @@ public enum TokenType { BOOST(0, "Boost", 10_000), HANDLING(1, "Handling", 10_000), BUMPER(2, "Bumper", 10_000), - WIND_WALKER(3, "Wind Walker", 10_000), + WIND_WALKER(3, "Wind Walker", 30_000), RANDOM(4, "Random", 10_000); private int value; From e5af7bf6669552ff7245f21c652a30736703eee9 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 12:06:16 +1300 Subject: [PATCH 28/32] Minor fixes. Only one person can have wind walker now. Tokens spawn at 30s rather than 15 #story[1293] --- src/main/java/seng302/gameServer/GameState.java | 2 +- src/main/java/seng302/model/token/TokenType.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 795d42dc..965c57ed 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -360,7 +360,7 @@ public class GameState implements Runnable { private void spawnNewToken() { tokensInPlay.clear(); Token token = randomSpawn.getRandomTokenLocation(); - token.assignType(TokenType.WIND_WALKER); +// token.assignType(TokenType.WIND_WALKER); logger.debug("Spawned token of type " + token.getTokenType()); tokensInPlay.add(token); } diff --git a/src/main/java/seng302/model/token/TokenType.java b/src/main/java/seng302/model/token/TokenType.java index 0ce24d48..8ae60dcc 100644 --- a/src/main/java/seng302/model/token/TokenType.java +++ b/src/main/java/seng302/model/token/TokenType.java @@ -9,7 +9,7 @@ public enum TokenType { BOOST(0, "Boost", 10_000), HANDLING(1, "Handling", 10_000), BUMPER(2, "Bumper", 10_000), - WIND_WALKER(3, "Wind Walker", 30_000), + WIND_WALKER(3, "Wind Walker", 10_000), RANDOM(4, "Random", 10_000); private int value; From 6f62efcc93059edbf1f3dc95d11cc48ce0fbd4ce Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 12:44:22 +1300 Subject: [PATCH 29/32] Updated the icons to be more uniform and transparent #story[1293] --- .../java/seng302/gameServer/GameState.java | 8 ++------ .../java/seng302/utilities/RandomSpawn.java | 7 ++++--- src/main/resources/icons/ayy_lmao.gif | Bin 23971 -> 0 bytes src/main/resources/icons/bumperIcon.png | Bin 3580 -> 7950 bytes src/main/resources/icons/handlingIcon.png | Bin 3205 -> 9321 bytes src/main/resources/icons/velocity.png | Bin 4328 -> 10136 bytes src/main/resources/icons/windWalkerIcon.png | Bin 3939 -> 8965 bytes src/main/resources/views/RaceView.fxml | 2 +- .../seng302/utilities/RandomSpawnTest.java | 2 +- 9 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 src/main/resources/icons/ayy_lmao.gif diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 965c57ed..f28b29e1 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -1,8 +1,6 @@ package seng302.gameServer; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -26,8 +24,6 @@ import seng302.gameServer.messages.MarkRoundingMessage; import seng302.gameServer.messages.MarkType; import seng302.gameServer.messages.Message; import seng302.gameServer.messages.RoundingBoatStatus; -import seng302.gameServer.messages.YachtEventCodeMessage; -import seng302.gameServer.messages.YachtEventType; import seng302.model.GeoPoint; import seng302.model.Limit; import seng302.model.Player; @@ -359,8 +355,8 @@ public class GameState implements Runnable { */ private void spawnNewToken() { tokensInPlay.clear(); - Token token = randomSpawn.getRandomTokenLocation(); -// token.assignType(TokenType.WIND_WALKER); + Token token = randomSpawn.getRandomToken(); +// token.assignType(TokenType.RANDOM); logger.debug("Spawned token of type " + token.getTokenType()); tokensInPlay.add(token); } diff --git a/src/main/java/seng302/utilities/RandomSpawn.java b/src/main/java/seng302/utilities/RandomSpawn.java index 929e5766..b09fc032 100644 --- a/src/main/java/seng302/utilities/RandomSpawn.java +++ b/src/main/java/seng302/utilities/RandomSpawn.java @@ -16,6 +16,7 @@ public class RandomSpawn { private static final Integer DEGREES_IN_CIRCLE = 360; private HashMap spawnRadii; + private Object[] spawnCentres; private Random random; /** @@ -27,6 +28,7 @@ public class RandomSpawn { random = new Random(); spawnRadii = generateSpawnRadii(markOrder); + spawnCentres = spawnRadii.keySet().toArray(); } private HashMap generateSpawnRadii(List markOrder) { @@ -48,9 +50,8 @@ public class RandomSpawn { * @return A random token type at a random location in a random radii of the set of possible * radii */ - public Token getRandomTokenLocation() { - Object[] keys = spawnRadii.keySet().toArray(); - GeoPoint randomSpawnCentre = (GeoPoint) keys[random.nextInt(keys.length)]; + public Token getRandomToken() { + GeoPoint randomSpawnCentre = (GeoPoint) spawnCentres[random.nextInt(spawnCentres.length)]; Double spawnRadius = spawnRadii.get(randomSpawnCentre); Double randomDistance = spawnRadius * random.nextDouble(); Double randomAngle = random.nextDouble() * DEGREES_IN_CIRCLE; diff --git a/src/main/resources/icons/ayy_lmao.gif b/src/main/resources/icons/ayy_lmao.gif deleted file mode 100644 index 884b1f5098b7b3acaa6e3a4e984bde490f32e7d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23971 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 diff --git a/src/main/resources/icons/bumperIcon.png b/src/main/resources/icons/bumperIcon.png index c1da6d4cf8d72949af1b64aa7e60335804d5d7b5..1a72302880f8d7f272fffb92548db8a422240c6e 100644 GIT binary patch literal 7950 zcmeHM`9Dx`igLKxYm>?GUJWDkQZF&NAUm3>KBQYj2W z_M+al>{*g5-+6!jh0l-Q&wV`Z>)d{8P>2tONN@))TgN<$m z+Xi|Chq>MNqzelRllKYm4f1fqc*+Of_b%Mj0@HBlY%EYlXJ==$_y2eNkAeT646sSG zEYKo93r1Sm($O=VV`O4x0i0)LW9Q)H0)n_N@bL2S3kZTQ3JG5l5fu}cxGX6peMJUx zRaQ=3K~YIr1*)ocP5rutrk1vjuAV;3z|hFp#MBIKZeeLw^nx2`R`}%Eu;row&7MGS+R)4OoZ)|RD@BG@`+duey z_~+>F@yY2LU`mya4jf`;sE-m5+~__ZYq@{MJpX$rse5ujXX-JMn-}f?&kD^VA}gF_ zP{}+e716Tz#w$jMTyaSwnZ=CD4hGHGb+6YdyQkUQ%ktlXhO}w+Z^R`%fEX4C`)`b) z#Rxe~esh%ICWAY)-AK>i+7|hHqjUTv!0@7P{kVUzDqzh)cIkyp7s>%`kGi{j#frxxRd@0TF7$R zSs13vMQL0l0o&VN#4f$&A_xv69Y^S43fkz8%3N5hWPgHcv1srwMLEY}0D)^b)Nz~x zCNk(VWdVrRvi_u;0z-SK)cSG}QYstW*s=1P6&!E$SKKdeTwx|W=0cko0W6R8G=J)& z-K96wSzs618h>yDEd8p4i$$S37dBeKunW6r6Jda*MJ0#Bd0@eZQVw&m;CmeY`E-Po z@7Ejabg^n)Bkldb>DIJdxg?4S5#|`8f;HwFEz^D{3LVYj(`F&yxzLYW)Ufts&yi#? zU_nGY-*F}*;px~QRIWiD8w%beI)uT{)ecgF4(tSCXasc81fU%vUn)5D7#R7pHN|Tz zk%Id36uJNg`0r~N61#y%jxPniiXr%(=DgqKfHGPI!UFOtRtV6NV_tHyw6-m9epR3O;N8t(uE7h2&5f#8eA z#4O;^PZ)7mbZ`pz8BFA6ImxH_UavI+z99 z#T`_w%nb9;MS9X>`=yYJ)_^o^Lt--UsJ#g8FSciw0FM^iTfA<8pP)gVv+GKrOkPA? zrz5<4h|*w!x#roBw6R@e8Ou83ECAmc(fq6b^m^wz75y z!VyoIv?5#3tQPrsEafJ%-TiFQy^xFep9U*6*YO(qD>Xd$BpBN7s$C_`XIt{~ILc%L z>Z1aUm_kyp6t=6thIA3zmCTP^i~)jY^zct<@$2K$fZ(WPNa8!Oy>UF;Qfx1T86OE- zsWH{DdzMIpm%qp764y`j;fO|0{7o zo<;zSm`wv=!aLF&lEQB$QSzj-A@zFb^|R_%K$u4$(o+Q6f2jh=3y5}x6GLffg%cmr z{ISMgqp>7s$4ASs9a-o=Z)ME|G9F90;R|u5W%w5=5&-k)ASr`j9XNr zc@$PFbHF@AY(2Guv%fAJECj|h>G=Mg2-Jx*pt|FH&&uKYN-Gshu_EE|Ckof}h1RTy z{S@OlliZPv>-)eKFk;mhT{h>=ocR|e{v9>)fSiiuD;V?!mhhMqIhx}gC~sF)1ZJ~X zan>S8PZf8U{qszyDHPK^pDTE{c>n$&;XE5RC_}?`RjaS@zKYM#1)K>-^%&dB^&WpH zsZ{m54N~6DMQ9!-wP zr|>oDAGamLST)tZk7*}!e>J|4R zd>Gq<^-6uuz7ivp1EZ^vi|ie}PlXcqr6~2<{5=zD;G(3%U9cTzk4Zv_fW^XX!-42M zUrYbPmvPL?5~q8_Q5nUR9HT^bF7dJuos*|Gm`{&k37>#5)5uljqLo)#5K9XfBkC;s zG}S!b^%I)HzEdTRKl;JQe`f^y^gQ6r@T!TvF5`=M=5gSo(~1E`p{?g9&_xi|`NmPu zH3rj;PK42T%Q8UWNPnqfVi)Z#jC)2{ANf3wghr^}?O?B!$zCtJY zg;>2v>h0^_M}$%2G!Ls!`Kbb7m+%@3fu888V60Iw!%-{0zFaB7 z|B&4I1j3^8vpD^xeHPqcr)Ag2*$LaLesriq-#tbgu|I@#WK}+Y&}_--{v{Cg2N=^) z{N~G{X%5@#Ie{cx%Gq z*s%02R0gMQZ7cZ(kbW4iwl2fgg4fV1-!}rHq`|eZ899TEe&5#krN#UAN|6#mJE|KW zVrQ5~O~M%QtwqMadriEjUj+rTak-m$Ktg&!t`yF|bFEk1G}FFFiC0IVbHbD#Jkak@ z%kWE`L_Cj$ob`el(4UquH-e5<;z|d$%12DL5e=DQH#PTAeaRuHKLREe<=Vmvh8VR1mmJVI5Km^Df-h5Aqj&SBK`<9K8 zkxhTYUSWB%P)0@XIf=)c-GsY-vX1L+3+_7*!-F>Js*twFL`i=Q8~RN_!*T-PXXsHw z1CtfBfOE&nCvhcI10VTaf#fm`y|n27D|ZA`&R#mg&s=zK{?&5%dAl?RLiTzFWOxB5 z*+?o?n$5B*PXyc*Iv1e>I$Je05q`LDVOWnxJx!2!R_^@mty~zj`dYk?!`oOLxkL}0 z3MyZyrG8Fi31kLiSj;dTqJ8VvC5~6-VmP>_Kwp8zp~-6ojlh}L?|}GA>24gZvKDGIOV5mB7g*^x!XBcA2eu* zue(EaJAuAV|1OHqsdAJt^0y<4e_B{c9@gocQ-j z&l)bd%oBsYwCLR_G9*62bV{S0V$`dddjqam<%I5cJX+0(2R+8&bgPv?@6G3oJ2+9X z3ArO>i@SnZbCL@ZO`kq%Y25j0hQlgUzY>yOF`nt~)%W)q>>!ybsPO#mu7CCV4Wv{; ztt1T){upxovHG)R&1pM|0OwH-U-;I0*Ui1r=5NWOnUIrQ%K52lV#krTq)S$m#>~*@ zb1bT|r9K^w3xT!2DRmb?7IR|0tFa>=0WDsrc0TFIl;)7Qgs-IzXL;!{V@HVxvR1J@ zQyyGOcfO$v^N9!_MJv=QaAO+DK19kg3i>LW>@liC5N@l|uSI6oDKYXxq03yIMW!j-kT_~kT4P12KX4bxs z9v{7&Z}&IxgniIg22kbw?*yq^l_nSURWrx~NB*v%8GYYhL$OTn0$o35KFCd0Y<~>r zaNCpg_obKK|KJ9E6N+`U%cEqycSF=<{1S{0G?#cVR5{9_s3B1jD!232Y|vgM)1Ql1 zpDt`9xg^WYVT0Dkf6G_|sgamag3KZc|7r(y|14qe$F`>b1)$!?FrUO0rFHcXB)z{= z6l>4Y4YI|fjO>GvI$d~=!y-!^ZJ=GALov{+x>%bhbyrd=c}DBxLO}9V(yL2do8|u2 zz4!RpiyQJ{W;%j@e$c5B#dWnc-qGjw(+twsn4SW-DHlPQ-pxxysBR3@GVzB)#scq6 zIS>gpq0F^Yj+Y4PQGEW>-xwC|ox=|$)gLtaYltt!*EUL3$-sg)y&I()gnP=w@Bvtn z2DFotp>a?7pT=rw*u;G%|BtW13z^@E1Ez-vg^bWz3&YE(5|bL^-t%TBoTKSQY#`2> zYUPWkTGlh0utQ$qBBPovvm_OWb*1oMD-(eSdicTZsxkTLrYhNZq z7fq1aYgo9?b`_WZ#l&s0cFLETw=fE0*mHk+ zQJcdpM?>q;7v5W3-gYHsYkWL$Gd~d|{!=8GTBsR5_T@?1c3uj4W*Jbj)=Wuv!Q6Up z+2{vnxRsl4faj0HK6KFfN@k(Pl4wH2nw==1&2?Kv_f;Kp>09Kg25ybH8kV0}-ylD; zq=`^e=x*Ala=DCN;o{#^GCgL>kbJy@;XL^utc!2nfAkmJm1iCG?2!{W#w3Ncqt}^B zg`1|nPea8B)589!`GN{JCK|n%w%2Gu%YxLfn{nSYjG22LFMIc28O)sa^~QgsT78rx%GqWyM-7?+$*GK3 z{qn)a@7pBI$i>uDxp(u8*_+M-6s^?l>c&+2V-(M3L@Y>|F&*A@H}aCfkB!F|8Kpw| z)WoQ1B^XU5MGY#UDyXM<5)ZgtiM$its&)c0+4GrTuG7zy4WFbJk*%3a8}*zM>mC)2 zdMn8;N8cSa+{~k~4imQuen&IRzRG;^OA?0q{nff*C3OoTu`WMtvK#Qo$;GV?8W;Ue zL8Kur{iCTdBlIw>azlnKZ?+frhwdP!b-ugpdhpPKJT4XfX0$PHA;^vtQFBogdDpMXfhlc1JzMa4OK-e#?O$t`8>a7HyD~fpnl6F*irspZ0#Z5WRq>_c z@C!l2uk`A#Bk{7K>xK>Oy~3`_?wmwXMQdV^ruOz|Be7$(Y-14QRvUyK-I82LW;Y(H zl-#X+jo-O*Hd7b)?R(k5#KB6?S?Pj8=h4B~)GmME2jX8Tyg{Ec-a6a3hc{ZF{yjJ8LA{W)vcUTcZfWHF#h z+luYzE%m@a!6Wu}Z;Ccf+f)ra)6dJ#JkrB8g4FaRlQLP zGE*COiKqUgc0N>x>?BVyr8@pmQ83rb5$x4_rMv3PWWrHC?WHa!ljn8fAoPAr;8g*_ zhe{#iG=v4-&ekOV$cMO)i#e)lOC2&67}K7erXjqjA1ZOM#jw%(cm9}ou4*+QtEdbq zVOfrI*1L!p#k+psDV~v4yQAHiCPlPalO1MS0=mj{_+;{6|8*)up|P4#BRR`lcPTd^ zqomrNnX@BHs`%U)_pr#Rgv8I~CzhahE~O$87Zz&x#~pj!n$SbWjEFzEBo+aX)3=}Y zAjHN$7Dpep7-Enrq%kf|^A8m=62M!diPrU3?%Wj4zxSJji|k!pyL9*)g#Q{x&86ft zl-h45SMe_?%HHpK_}qcpU5|9Kr^HIXS2_w;-;gVK$tlI_qjYR2bafQSDxiNyb-GO@92v5 zwOMX}i>f)p$~j~Jvc+(PULTg~0}fd5eX7;T`_GO?Ghzh|F>@_c7va#rWho+%`iwXd z0`R(O*Y$|nRX)9ofy0SZN0&L@`42a~MF#%-Jlu5@`Y~Dc;zZreN z{k5-~&ml6DzG3Jju2iQI?!_RamVVb+Y`xl|+#57@0R>@sG-uw_4h+3zR_+D(35JZv z_fFfa#p&+r^=(wE#~3+l^oaGMeu5SW_iQGu@y(d&0CML!w@(6K_ShbA@-3FC zLE5Yh*!;jWA;9Jk3wgcJID(20>d8%nwgOnUY>2u=2kds6i7!00IezMiB|xmZeJ)gw zp6kgihE~%hN>?lMS^dp{2#fmtdCKe!TX;zBWT}!P!c_oHI#u1CHmy$~B|Sd6NTLi@ z?gE-}k@XtVle6eR($3LfQaPSE!(idxbt|e)#Q+}IyhrS2zrtQRa8uo1%dXvEa`Y+l zlq7IA8zsnDrEP%M6!mM1H7*9tvY{e?R#8|8m;NMj+C1SSAVAzMAd$LRI-m}STB6S1 zHe=jBXIB?T4JaLW0EjxGqSfQ4&GGh_X(H;6aL=CNPar^-oIj?19(v3N6aXp z0`&&O$PZYSEr|o6fWmwm7-PUoo?~)zV&;8?wz5*b^|BcLCR}tK?qcXI8?V`qtd+p}uXe2B z9?hqJK{)~3Txa|=2x_xWT2t=IiLU#$We}V=TT;uXEpR~S>`*30ypue^(ai$~L^%!G zj3+o{9oYEm7+x8Wb4Nc+igKwse?uQDzVDoLM{$<=OzOFL62Pp3gI)hzt8|2jWh<9B z^j*^ONSz&q%2@ECg~1R9eqdRc)ML5f)9-x=2aIS}sFdTFmm0oXphPDgoYpT?>KQa& z=Y%%?+6Lq_-Dtz{HN|%zo2jX3L{T9`{ql;D1Jqw>RxP&DA_-&R-dpHOxIWS2@56$I zD*#y1N@aX6%(qwhz`ddY?{6VZZ6kZ5@%McOlFNXcq}J@pV;4p@#b-;YiI^mB`9Z;0 zjCxb%(q6T^kP>!D$kgDbMHLP%F<&=UdE=|ndYTV44`E2GuR8M>sD2dS=X5chJn8Gj zXCymy=)QJY{htA&M4p%4-&!GY_MJ=p(D z#D(bECs;mPz5;zw!?Yez1Y`TV)}6k_Dzo>Zj{Ujj)od7l&;>WUr*b?;;mr2h1_V%` z_Dx&4M=)vX?LkeRNFVyEU|!_PqUKGdT-Y`5p7>gxc0q<+ur1nK@N-lVOn3bDG&k$X zEC-d-cEua|8D37Im?+lhBqlwKb@0CeZRGXZFQsJidpTPZ2!8n?|5hYAsLDY1R6j%D z1cI5OKUd{9y1ZTgT7m1YC$m)@|3-VHx*=-<;UXJ6|?k_ U4yb(b?=Ga7k(FT`>~{SB0iX?9EdT%j literal 3580 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 literal 3205 zcmV;040`j4P)T`2*BqSu!(a}6S zJWftdn3$Nix3^+qVt{~v!otEsLqnydrPtTjczAdtBO_K;R=>Z$92^{-ot;5JK@ANJ z7Z(?hkdSFw2O{5Y0BNC|3~?yaZzs@%`U$H5nbF zP+iqs1%BB*KPL|Kp)XZk*OwPbc%D*Tu`SE=pGTx+xh>8=@I=QT29;=CG|Lud_gFTC z)`=Q=z$7HPs-hKw++VDss?y2O1IIz{f;1u?1HEsAbu=`cdt?zCy9=TxTCIM;u{a+wFZ`jYouTwD7U6eB zywgL=NmWhkBm)-tq#BZ=SCxFHOBcej8ceOLQn~eci#V-@Avl`#!tadPXaIieX>a__ zn6CS!r!tye&4E?cRFZwxP9ufDW0&KPe@jGL?5$C4@SqX1K9tiJ7 z-Sm5eK4RL4UXpcxBCs1XIf`mAG`T-m2(8ovV>s&vO9>-@+Vn5vyRq7^PoozMB?Q|M z2h0}^FK{|z>bjm(KQTG|o693-ycE62mSDInE3K2{!KSTF|E6_SE^Tj^vxt+TogHS? ztc_lYvu9)7*lqDSA3+N#A1>Dv9nxRh%;l7cpQ3)@X+B<9yk{}ash@Jia-t?9+FSDp zY4&d6uc>ye5rbeP%K7oyP5a-hX?IU|*-s)EZVUHsniN~gmUN}4C`)vqc`m5FrYOK8 zx4lR>WFo~UpFBnIgRETMgg@KHT;#66??-!~;b8^+TU{_xyP&Awknf*ct{xA&QtG43 zeN7#)nyZ}xhgG(xrQDrcm$-dmrG_gc!L*XJ@cC6+Bu*Pe`ZC(!?t_7}1=9~uB=n)5u+#ua&G~$6P2^rXh z0<&odD+*)y&E-7^x7Z-ARd`deYRpd?^-J(X5wRH?>{blSVs_GhTvjqMj%pg<9)(yC z* zI}Sj>gd?chJgQ%yudp6E`=|{HoWzH4BKThT9Qx=zV0+8`JrVSV^1HBk0NNw&b2P!v zG05;SAdDKE!l%nvy@D`oaMM2HG4l-t4ZFVmWdfIsYiMEI=kRC%tv*z58O?YkW8Ha z6^VQ3L%5AXMQQx`@zMI|kB`qdjofB4ftKI?`ZMUzb;#XRD*6PTe8Fd=99TjY-mAQT zVjZkaz$zhRXuJj1ft!$l_!=v+1FDfs?B1)Zl9++AwJqENm?mB?w?x3^VV7X6(II!Y zr~|9Sdno1lk8LRdOdh$#;>q^J*!L^K(!y5a1Isl1J|-ap4gFS;HS_k96NPw0&tm{i z(?8~#3V0*+eR_$FdhI4jwI@KDTV-Mxl3wK%@2>Q|@K(?YoB^8K7egp$e2IUJN(@c` z5+aK90HO@P^a&`=5`B`Y&4D+^`(u=$l_@~zf5l`%QF_P^=@$oqDlX$AvW9{WJVe6` zahVLq#Tb2hhILISn6)a8C^Z0Bh3EedXy=Xj;mNVV)@OS^WSeuYrQT!*?2+`EPY{(N zfJnpB&Bz1(Y}$s&Hjk0g;Un<|-{P^!uLo?z#(VLAlL%D#tCWyA+Y+pa z38%aw1@&XVT(k6`zlQA$eLu-2q`cWt`;n26w7b{kF1cL?w+w-;tqlC{aNa}NlBiaH zj=SWyH^$r_Z%{ASr@j1e`oQbZ>;B_S1e?mTsyH6goG#VlefMHld;0TU>s3%3cUg8r z6!5fp&`~5_uVy9hz38Rvo#1wW= zx#9z3soOa2bOU4=;-ZXj`FFOEWr(|dLAM`wh%$UfGUPFSbU>8h0cURU4Dq6F5M_w1 zb0{h>L>Xdz9vjI)NHRowj|~B_ZjfYHW36S0Q%x)b{%7cd-_P*(@5A4Jmi`Ci;qTXJ ze!pJ&`|lS2f&1Z8-0^?Eo#yx3+J3*iRJw+|w9VUuEuu(-8>IXeSQL=&i3i-iCn7)( zDH9m|hZX=mq-7|bOG45YsiXpD;2h`f3iGo$7 zP*YqROKjo~XhglYR(#sE<}k%3GR2qP0#{e^0ik6JIa7QoUySe#tQM+m!zxpZFl(H* z3q{);IdhyhZ>;_W)P)*aw;VIa>T~B?d;?Qn@&=#QRVfhp7W_FgU(lq5YM-cz$eEGI zd-{4cfnY~L84NIaPa?T%r*(=qAGDSt!2y-KCY679M29n4AKA$K!mTaY7O4z-a?Ems>0!|A=DfQ1KO=k1thI0cQlV|AhFO z3SxCJFew=``^V$!Vu;l?!T5a~s`gTb4UWNn2pF%3L)l)+sHJKQAsE^}oK#~-s!o(7 zLt33Esajux42iYAr0TT^GNjgP6RNo<$B8niNXgoPN|)e#n{1uetSvKF*RJ#QI?w)MOvYO;$kw64i6QCDArp+jAL zf!+uL44rx-@b|aiXXx19g1tuxJ44qVCG34>xEZ?lnc?mg#LdvXR}goDk5Hca4=HQ{Oe--g%4+*?Q;k_CMrf$k_jowTC4eL)IRaoP9{S81nWZCD!bn}MNBUv9SE>dp+sdaJ*v09R5ktw4^B4E+k1 zTQ>_Vd`BLV;uMTrc4MeZv-0sx&??rz1HGm(d7uYXNXVRgH2Bc5Mo3l)%GRr7qL<2Wv!hKQH94z zO#B3?F7g$tW{nBIRO`kr+wCzZ$R1uqsBGO6X11zY2SubB-XAQLZHS_H<~#{8H^Fm% z2Zz*9cC0oM4gH9+UlJU0z72|^E=s~KnHB|%saPBo!>Fnlm1tcw81vk+DYQ})foe@N5L??PTh#Dm_36T&bO7t2%BoT~W zBN-3A8PE5AzxA#6Pk3KzS##`j?Q55N?|bfJp9E85T_6QB1s)zAP+w2W91o8Gdr5#t zMu`3Bt>PWTeh~U=>RXbLku7~US;PKG?xXj>9}7>%{o)@OJ@vv0nF6%$2UvK!1_VL< zT=0T|g2WzsdH6d+eO$!6{oD%om6@@u7*mL)_Vx8O7XQCL|C@pTX&JETVm-&kd>LSF zY=MVQKuAPPLP|zXK?wj-QQxAuO-o15zzAYuW?^Mx=iubx=Hcbz7Z4N@7P%uTc2`_N zQc7AzR!&|)Nm)fzOZ2r)7?F*P%{u)JqwegA>YLt8t02S=!r^CK5m zH`rr$k0+j9-afv5{sDpTpr^qhq0hp?BO;@sV`AeF@y`jpl(Hyb6kcCgNiA3aePiKqdHGCsLSv_1g;Lo3p7pC+-*U~#%Y$I0=!8eTm$U!w15J(ILl1SGUQFAp?ZHZV|j~S9Rz!2 zL~|`HEQFR``j07Ighgdds0c2))~HMS6D54bzME^y8!9fEs`h(7q_ zHQN$L989ZwHtd;}E>^ChYi!r-)cZR|28G;M`wbLffUqq{C7Qn2R<#@vT5SFl<@$YE1S5xmjH+3@HQGs3+NNT6mw8)$YoEn1#X?{U$Ep5OY zGg)EF0`a8L;rz@Ia77xeFelvx0EF_HJ!A)BG*bn$WoMzO?%J|OhGM`z$(U?nC{Wyw*P2qK|zXz zOYWKH1JO+Ly<;d@AG+nA6^K))YoPYv`;5D@5?^MUSlKiN6R6G)UNQ{1KH_?0-TGIz zCZ(>_HZ`3sEVrb}YP+O^gsUFWbZ;irMvj1svOMS$*r+2kh>pvjXbpm&asp>GBWpEk5rj(8LxnU9ejWruve}oSlh6Bo4x9ktdzD zBXp=E#n`bS`)3FtA*-iMnr6YVLOWYAXg%MGZ*B%lVIt-anr0!v9-%znvjD>Kh84Na)<3dM%dJd45}M| zK4x-(52&NAS{P&{XA=9hA51JqssQrmrSKG!l+DM}Ycaaq6IN*{5b@caIcurzC%gDY zDNRnA!F!GNf9manI{IVHW!M#mgq<`M!a2m+4&-SK%otGqKUMj&UWiP7&gO#%UUvGnP zD)wqgGm-F1(G=&$tA!$&-O@CC7wR@r)&?+UN#l!@=g;Z%X$URY0XI_BxwH05U~u)!U^X ze%rNsL~!sTp|b&V2sli5hftB**Rp1QB-OD`sS2q?ZS&bejz zL{RMOCE3b7f%Hckg6+Ez74IXeEC@)5ibDuSjmn2-Sa~x~x!d=yk#WiA==4Z@jd-|Q zd7L%+U3ma+ig91SN(>CmuE>iugRF%;X123lCfDb7BV9Sahnm?ZE5-)5H8M#Bt5sWR z#qK)D&G-O^SA+m%eK(xfCzPPUnu-h6-2~o=O-_|V0T6mtPZxKmJfwFp^t7v6RqyGX z^%UiRCLei|e;1Ldd4 zg~cZjnt|RIvnwdDl0Sinb{tEA3XBAB1|%2}>=oAT1Dw#5rfRn#ZtCgDIgY=aSA@Q+4JGe>J~tE9CS@ME#g-2otq}fYY zXuBSki?y-T2>`R8W43oP#4(mrjB~vcI*WW*eB)~Ggv_b73kUD>5>j-UL(c@!zw!j} z)27pb?h-vR1&%3!Qg}4h9!ei;+lZsOS8w3w>6dr~J-lN0gq)P)Rw*nKYolO?L%W3b3gdV+3lgIH`1tsG^L|hS)F(7heh$wRaeI=NX{VSi7R3^av6O z^|-^y4#1SRBMcx)E6xd90D(eEG};+E(GJ6$#hm~hZWok01K^)@t88t@N<+~Y%Ows~ zwE39hB^e@Iuiid3DjT&OFD{+bEqp-Wp!KZ`On*QU5o>s<9qPCS zwy_KqO4b?zHOp}y_C8TOed6}dXeCebu$<#ou+7X-$#pQBiWR3Wt* z&wqo#+f$fk7d>7n4Q*LVi3`}}NAZ_ZHE8;Solg7mqhR}Ld3p>zv@tB`Jgxd;^n{%6 zYxoCrW-(LS{9L{%Gifg&S_!@U24<&PT-#1=q|GobXY!pphDz%7E+qb9Ly}4f%3V4( zvy2!&!w`m@Zp{8!mNSu<9Esb(6uBx_b!n*F;$#&dGCk^1isan^FJZo+~QJ7@|>-3?K-h<^bSGuO;rA)m87masdzHL@;sPcR{;^J&38=Fjc6 zU*zrUTsSh*+d8t-E+$=k9ot(u@s9kZwd(qykOHy#9pa!@RiWW{Qh#SkUx>?Qdp!hN zCzluA2#R08`taJl=xlF=l{RAVN7gO84a#NYrUMUjY1H&{FY#QDYo+)m>o_LEZu<_a zYPPD7VQF$-z*YIjECCSWzV zi2PO%_4ShN49Pp9r)?6JPU1$?AxqexWMC&qE@eLHOpvN|KIJbpQI|3k`$C+DzT=%7 zg+Qk+$B4Q|xUtF&CUiB1km&*)Pdbt4U`}~stosTuFIRl^6tDlCDd~-*FJPYG^0{g- zo=*AOnMfSd<&ha3e8uQf4XNXrqPx0&UPfa0dlq;Hcon-`S1DgiaFWhoQdwYiT9%f7 zkoaFAoJYF7&~Y1#zP|0NVF5_U4fnLo{FL>i_4a_7^yDuYeidAF7=yDoHbZo4P-7o) zPqV(Kz6W@pRA`v5K!%$fs;b_>KM&+N;@1mC)?%_gaSz{{m*y+pd-6*BiYp zRJx$Vc$}|X5jke^gWyy&hT`czF!>qauA>%#l%4(Q7C1iu^K%pLTmTA{ zIE^wtOKfD}?dg+ub*SsHZ{^Mo&TS-!;O zhq2F|jkbGL>)3cIr`Ifx$>^c|erlE92Ah+CIgUJ3nBx<((b=;4L!I6c^Ti{3#exLm z`I9#s7Wwls{J$NIO^OYTR%1cAaxQ^7l85t>^%ka zw^Vfu(B0e&dUmKT017RFCu{7KQ~=|#`#vL;IjxLz`K3K1wi zea11IPjO>3I)*s6cjN%pI|)F!-oestO!aqu(_6vSiiDT@Wt-rLim_6ExyDT1t6IUTmm+onwFWP0==hl63LPQWh%NYjsw}0!ZoxwZxpTmX4X6E4mhLS@RCPf z8YMZJ^jF??2jf_cFOT*C^Ctk$0!p4jn8i0P<2X}`UNn18h%L&Ii*~4!GKUvY zEn&ik)^W9}t*g36xwLqBM~Bp2oic+$vE@=Jzu8c-_NM!o$Un4H?Y-oor;(ORH)ad} z$9Qb$#FOTOaek|aCr-;`kvIxqZl4j?oSN! zlKOf4JxweQcp&lMh8OAC{5>%^0%|K;k)JBjk3RKAbhU8v3*98=EnN=KVWCUDPw|J7 z+%)m^88w^Z=y`t8O##dY+@IDDS7%&KP8zKcNfrCYq1j0=^TmxrECeErqRRT#BfWiz z=-X!-#bzip6+h3-`hPb@2|1gSub3w%RkBrwQeFL_KtWYA({YMFU*mZW+L>ycvzmY{ zF1P9s*}YJA(af})d&kU>6#Fnl<92-2@zy&#>Hy6hDhc#WH>%RZRv5#ZbG?Sa79v}n zCb_opR**+iX4u|w`dC#)f0<-F874Aesi1O{GZ-6XNF}jwqvur`5FIsga3!E(mdfw9 zl|58ypTaudhB=;C>iR-FmU(_Dz`O;)wd=!~>GU%$)vmj8TQ zw7h!A)h>}zEHv)R&Bh@Xd`V&JgQixitY-M`|MK+y-briVz=MvGT4)Q-t8x$H>lVhb z?eHbax2N}o#PsTs)Kss8mwinCjcC$ndkxWZk8~@hK)PtI5NUPORu9hlgK0tTDY$fG zekCfBD@9t}#1T9?5bObxyxH=2-;r!)OFX)1j4!VM5z}T8kF;>Lr*9PaSpm!>iR=76 zUOBo=h?}sxeB0SCzLTmIxt(f5E3ZF!qBn*Xj0O};r21+^)2MctH7u;dFKX4kHs2Hl ze@C^iF}a0)n6EuGXb9uwo4k3W@;7^}$h`{i)+PXvAmX~W?uN@@O_ckyDkY1449i57 z5;ge)JqeC;csJ`bp8uhTUTvc}M)#c1e(%yH5+M0~ZKlL!ev#O0Q8FMoM zpc$S@ZL7u4HRjry+!8z;a}Re`TON8DsoPza?w(ZG%RnjmE`se{?KT}@cQ^b<`kDE! zzi|e}Dezcon!pOU>j@IPrP_wY8^e=uJkr{Rc$}%1F?jAZ^~aDlE#d825}1jn5yFr} zc)L!;ueRQkrj2*cWage2#>~34OD4$7`cgcxz=rx<3vOyrA)XOpF4v_UWNJa1p{vq> zsE3=I1Phjx5ZTHkg$Q?Py4Q9}*#>GS+t@sJnK75JwStI>_6oZTQHx!Oy+u*%)bPv_ ziVfExQ@=u00#U?`7d&B%OC3L;(1jq5)u%=923_*NQ$gEvf6VUs>79AH_i)iYAq}BG z>06D3dJ&gL2Wy`@24K2E@62@VzPHzJ)eB6mLDKe6P!{-P$$X;=^~&RwSB&SlTP$b9pMQqiSS2{4o|uHn&;G@`L=mz7 z5ej=l?fX6>hYbvDNCZatw+dol?8@RvoxwD|UtU$;lX*T_7onjXuFYW-L%Lt#&93w8 z+sk_rpJB6s&j(hgb*I;&9-of(J|AbWp$7v+>k_!x*_=2)mJmBclSwV z@YHz*pHwqki{d*S_4@o$O;ibCV1oi3{RLj0NGcynhS3gdDpyUm7_gRUk8`SuY?U?~ zc3ENE6o1_Mt0JltDQSZuK3V`@v&NEAO{oc7Q;m(51-i^@CmC1suop9w)+w933Z3q{GR0V*>0i0xKrA;#&ud;*gyNT z|As$oqwx)Y$=TjGMBd|{2SL~?j!%Bi;SB?dHmxm$Md(xJ%a9TC$VOL>aFHvoY8Z`$1>%hk9 z9ES49klQ}lWhE5pH#KWzVG^9Dr&7zZR?b3qCQVaOQ+wt!8RG6v_ixR&np$6`8`^m$ zb@63*Vmo2c;$i0Z7hh(qLdy{C!sD?blEbz2%>Q;%Vtj2w4Bk77hi_xnqBY*me1-qp zea|0`acz4L{@a$%EiM{n<|*(fYu+%@R5RATA;zmR_wc`!t1z=@iN{kbBSYm2s;+sP zffr)#UESaP+PAuGlm6jR0zWXzg!4jlfJo%G3F?evQj;H~iQnTg+j>X*Gmn<@qjj^1mZTK?T_ zY;#;QM%cU#y(PA#_@Su8CU)GYGKksXgDB9*MfpoQvTtv~a&dl+1znu*$Cj$YZ`~mT z8RvumJV`ElZ*RhmwDz0OQcy7qF;N1w9m|77i`tk;7FXWHl?dwMzLN{wNXzKYC1!do zQBG@39$TICFyS<>r3+J8oAXtG%n6N6Zt@4w4J zx?a)er2O$D{7JX-+*MXejFdtw9=HT@-%x}0(w|UCh!sxS;ul-&3}{0K7+J9L(^>5K z3d-}-G;)Jvs>}Jr zamP%gRYx%WlVRZsyVp-Z^IC<>@jZv37MHPg zKs7#u8Zi02IxT@WuPHA2mb2Ns#y((Z7lx<=$0FNUgIgFErK8|?bUpf~QaO5UaW%!1 z`0(eB^R(lBHOl#pO{Q;_?5n8!#_RiI>4rnsuSG^aYOtibmfahlM0~15-jfq;avszU z9QM|%pQ4^dY)-UGpnl?mh6{ePgBwG=Z5WbA%h{YK+Of|BsmK`xWtS_Kx-26K@K)xj(hM5zb2N>mO8k#^73~!@B6UCeMRZJ zq(&~+8Sj!2`CHJ%77t!A(9se}`*v|WB2aT`M|u|-ggvRQrgjlWT;qExu*TJDE?O!g zNh9m`pFWXWnKiu?$6rni44B&==(opTYw`9hH;NON%lci6DNAfzn&n_de@bdpI(5)} zylRSq3efgU+QRtGL9{)_C>MU(o=30^d;Zvbm?2!0w(B|$Ztp@+GpbXVBZYq&3&)7# z-_cz{NIZ>4A}avj#P2u9M_sT?$3@U&hs4mV%JUkR*YqgQM|9lDf*70C|xJB0^b0E0=o`_X(J91FY^b z+_@l+;lqL*FNwa|*$=3b!}P>W19N*3ZcKvsKys4p2lKuuPU$#i-az1!2Lqfqgx-$h zI2nmPmRW-tzwnz&B<_A$5Oi(@kdCtvAh(+s{&|fI3zg^4hAy2mvEs-!FPiUG6h+ZY zq&qqeNv(B-0g`cOe~zN(`_85~j;t^^jvR(Watzjg4<2f|@%V{Q<3n)x>xODcbsogb9>! zX{@eLSigF?ilyFaee0up5Q4!aSGE2Cn5(Q^ic@qm=@u;(*}98UtS^_c^=G`qW>+I( zg_yC!zyXJ_Zt5|5FR&$mL!h<9Y_oOMaAK2SAM15dj2@0JM1&ystf3+TXQ#Bi)5i|~ zk_St2P1hC1bTBQ4UAfcI3#P-abRCQ~79akYmB8dqxdRTKt9_{Rl^vsnqglu#P6O0b ztKsBn82rN#p3``2%$G-GA88l#9I&~XU}WJrG))u6Xy9mSw((6;syT4*_@l%7G+~@r zqa1YI9uF?IRIu)bcsnpC2RRd};8HOzvrNV@QniUgzh5{6uLDDjuq5`OJOmB_Le&E} z`Rik`L;Y);5JT*ZZ*_artILMoxL6;a_vpr*W?_9%xkskv(rI)TCpGkT>#=^HeTpj} z70Z0e(iC~{!7?e(=w;AYa^!*Qq!J|(*>^g2hscpn#ex9nS>bbwY~QQ4#({sRX_GvGy|*}#TPX?C); zGuETCe*_RUPE&={96f#EQ5g>p&?FfAn~qd}mKPH{al#44m!GF4FE`->ch>L5#QKKG zfK}@|*~uRr>LZ>7V&^(Thc<|3mP)MTt`HS)XDvretdwehx{1@zjL0f!$@QduAw4|{ zC%|7$Sz>bcXDZ;5dXBVMnW`MFwAoY*0+#R>Tqeb=sj;$c1zgnksCA-0biUvk$BxNj z=QcUP>7Lk8YKoY*_@im; zgf(Mumjv+yh8?}ydo2&7yI`lX@RmeSON}6O7?UMFGx9JjLz8vCJdy{PES8q*=*p}PU&{C<*bK{#ka+8dM^Jp&bGB= zhYl%X92GSelE%gP`Y7&#w_e?4lp^;0NQIqzXX~L7iHTi?F)Op-+ zlVr{r^Md(dx@K=bX3hTc)g_bB>{UkXn!x{z;5Ys=n5X=I)B1Xhn2J%uj7lg_6CO|) z5xZD}`SOcwVfOn~$BgDe@|{>#roZNFnYNchlQglcAMQlRWzLs8u%k(wp39k)6q*&| zK%}$$=T(9^fROSCuV0&wpyqyJs=ZBLrRm#%06H$}%Cz8pLE7cX&HhTB_59@5!zSX; z;`^$ygj{KFSvh5XR~z?#MYY&185n+T odieGURO8!cvrE4v9+!V22J#N=`iq@&aPLFuYa44dYC1;$ABZ_M%K!iX literal 4328 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 WwD@H(bH7^v0000IV zYZRrbq@}8{*X#W|-uIq!&%O6KpK(9W`JCsRd(M4~vN8rSLKvy2s6eJBNLwl@nu{$B z6+nBj4B^E`FBaMmgsD9M0Q1Y%-!6^}fhIRYF3cbO@1zbDO4Gjp`9cjHL+t|4q2cbq zo>bxC;R-(fej#_=13eW2g1w$@X+tjJP@ydB4bRWdFYNz+{67Q#HyL0O>s-F5{5;gw z%8rVfhL-NqWqN>tk%<|^!U|?%=iubx=Hcao@CyhE35!5Q#l$79NJ>e|$jZqpTvb$3 zR)MLisbAC3)Y8_`)zgO~43LIK#wMm_<`$M#)+ifWJ9~%gHym#|Ip1<|z3t|H=dOn* z+RNL=*YDna|A4@t;E>R;2jLNsQPDB6598t=B_v{!l2cM2r=@3PW@SIg$<50zC@d;2 z!9Fc5E3c@;RaMv2;_C?Y4UJ9Do;SZ}dD+_5-qHE0tNV3NZ(skLfx)5whDY9xzI#9R zVf^DK;>6?>Y5Mb*nc2DduM3My-my_p%>%nX@R zg`BdEt4Bfkq>-f)vmaf!bcFiq{LUhzS%&YX~+!NHLR;oV_-t>^iVMrkikar~^{fEW<` z!M%^8x->D9?`Jj^*1X=srG7^rtvF710+@yW)yJ;kr z^#y}6pH+daID&w7N)6Pw(SYE|0MZDi#n_saRzg!65mL4MnHu(&5YoaE#fe-{d)rkY z^RN+&G1DYgat(lGBC{`#V^F38ao)sMu#C@UrBaqZSf<1%aRp)^C4NU8q{+>IdxCxd zX_ME(Ag&Mz*-%dQjxr;i3pCjjwr<7&B=sPxS-4YB#@lGfZW#z67{=~R;suhljjIJV zp(M7pLZhYT9+&Q2m_v0KR4JSCEdgT%%tWpu9DOBe!443lI0KFDQ z2_fkOZ@+vRM$}++oa|6TXbgB5uNbuVE1WW_WnfMGLH|LU!3Oh`|R$ z5igYs5#A`r-MJ>Fg_JU_N!^Fj8lqnN^rfCy1 zF4qUPE(R2=&Kutq*cGu4ewsHTmxOw+1tVx+%otA$?mkRLy%)5COi?4GReU}Os{sX^ z+WL2eFeo!ENzg0@4KBX~hS$T`GXAcE8jFeK2R}_rW==$n!63mM0LE6d%6n7_X#aYr zj!kJ9>|0`DP#%vmpJ9Qtj9tbRKF#$;@&LmbwQT+RQdd@4ArqVkr3)tBumjT>mDGC; z;53Fs6WYZ`D9dLNWS$!W2-Q!M@e|+%2B(X5#uyOrP6E5X=y9b3q<~HkLbbzz)GUh$ zmT{@^_Y)!gqucenj4S^^+6)19^Yx5-e@|b4QejW2ag_mtFF#hmWa(IJR4*N_lAiEo z!VS12k9T!}a@va~YsNC+s{BdsgcxvD+4WP702h}IB9?{TfUvGWCh;LmLkOSFMe^;} zslr-r#G`EaCByh=arkh;7)}I`__1W{vXzXoGpFBmWW*8bNn%~v7_8_6o9rSH&S{dB zAPXH_%2#`L9l#M@Y?7A!00&jGs(^Q%Pvmc|*lwkDhzRE%b*zRU4emKj^oBV-kfdsfXAObct6_CZK}jgb zZOtPh1H!Z5>g4M~uvs)_v5YklH76SN(>)ef?}&+Ts>d&WWx{pWQdSIr>4FTL7rjV+cu`)tLlSDynbLF>vookRQJn-P zH^yX@3nY!wc57~!TBd4$3yazjM+$*=WOW>wb9n5p8SYylRv82ZLYsw<<%(HbI-nd$ zx@UOj+wfJ8=biPBH~Eo^fhlEMu!K=Dqlq#$a3tpbrG=aOCfAVVs1;yAQ>C2_=}HtW z*%I_;$>GtqX3afg%Hff#m8_T(A?+*cKtQ-2{5#6%A`2qu(%xix9+wD7E!|Z<8Rhg? zmLk(dgL!{uE__9w@cZgfpe7B%#d3Wr`%k~>!~dT2>1ZuYwE5Svvt5W+n$hf4p{35v zFT#B%Ztu6x_S=0NTd0rI@PaC=0;j33FCO`f5+IxPE-fK+! znb+rPViYk;3);VELYRy{RE+8Is=tVC9Yq0gKx_w zhM=iQQRj@)tzj=fk?@JXEwDe^Wr5Nv%L3CXv}D<9N7|sS|3Zyc8WKJUR0SKsgV;Wm zRVkSdLX1^1Yt{~Kj1D>17YkRm5#evWi62B@p~}x_;jf;bn#p)da&n&>P(yYZ;}0#G z`6hiSKFZTdtnXj$W>nc^4G>R;D6a6Rg4uz9N?c3})&O2siVBSBNH55d(G zUo{s{71RAwFL%sZU73k-ML{6E8BZ={A zj!gAJ?nV;d;4`sfn;+V9qv7yNlCC&DE!l4kKTB*3BCbFDPiW1mXe%M=UhEz3_eXaQ zzSyHle6H=KJY!#fj%K2)8|7hMkq>YEV^E1=*X4{@PrtUN;5=raQBXvzxdeUX&!rLt z>ihB9Yant>Xp9M=dTB(GSmUEs(~`$Go}z#gTq||k+ICgsy!$IQ1D&a_=~X;ow$2qg zPciuL_xDgGmiwts;VDB62ie2uwL3D;G`hHY(}!i){uXlTjuy1n1kFyLNY0!cDSdzV zK$jqK-uM@`=`H3#%rD_ea-UAVAE->PIdj>!MJE`uc0U_wz*?r)HiGjal(P}&oQC{ok!%FkFgt#uZ!{*Fn ztvchQ`;}V%EDO7LS|2P7{N8quAV}Q1rTlEhSx^Y6(pt7%!M<;KxFOp5x%_oy@f9ua zcirsU;^ieS@o}@hI-0Su+O4BMa@Fv zx*zJa#&Yhbeg1RspSj)A`AJHqjnZF=++SjH^CI8G@yBjM93gezdpg7I;AO(rJ>!qy z>kUWa_qD<{7QdgBv+5jG=$>AtY|OSVJOlaJOL0?APtU{CbLodV?}z@BL#pxfTBm

NjDcovL8dV#(yV*wW1+P!0ZtW(I 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 IBis`H2b;K4)&Kwi literal 3939 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 - + diff --git a/src/test/java/seng302/utilities/RandomSpawnTest.java b/src/test/java/seng302/utilities/RandomSpawnTest.java index 60966ab8..a50d3b11 100644 --- a/src/test/java/seng302/utilities/RandomSpawnTest.java +++ b/src/test/java/seng302/utilities/RandomSpawnTest.java @@ -39,7 +39,7 @@ public class RandomSpawnTest { .getDirtyMidPoint(compoundMark1.getMidPoint(), compoundMark2.getMidPoint()); Double maxDistance = GeoUtility.getDistance(testMidPoint, compoundMark2.getMidPoint()); for (int i = 0; i < 1000; i++) { - Token token = randomSpawn.getRandomTokenLocation(); + Token token = randomSpawn.getRandomToken(); Double distanceFromCentreRadius = GeoUtility.getDistance(testMidPoint, token); assertTrue("Out of bounds token", distanceFromCentreRadius <= maxDistance); } From fd53fd52a4340212f44edb62701c1ad9be94ea22 Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 27 Sep 2017 12:44:50 +1300 Subject: [PATCH 30/32] Forgot to git add the new icon.. again.. sorree #story[1293] --- src/main/resources/icons/slowedIcon.png | Bin 0 -> 9582 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/icons/slowedIcon.png diff --git a/src/main/resources/icons/slowedIcon.png b/src/main/resources/icons/slowedIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac48acf05e175bebfa9d7e1b07c8e6be81eabcf GIT binary patch literal 9582 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 Date: Wed, 27 Sep 2017 14:16:27 +1300 Subject: [PATCH 31/32] Modified timings for load screen. --- .../controllers/SplashScreenController.java | 2 +- src/main/resources/views/RaceView.fxml | 543 +++++++++--------- 2 files changed, 277 insertions(+), 268 deletions(-) diff --git a/src/main/java/seng302/visualiser/controllers/SplashScreenController.java b/src/main/java/seng302/visualiser/controllers/SplashScreenController.java index a3df3b4b..442d4083 100644 --- a/src/main/java/seng302/visualiser/controllers/SplashScreenController.java +++ b/src/main/java/seng302/visualiser/controllers/SplashScreenController.java @@ -37,7 +37,7 @@ public class SplashScreenController implements Initializable{ class SplashScreen extends Thread { public void run(){ try { - Thread.sleep(1000); + Thread.sleep(2000); Platform.runLater(new Runnable() { @Override public void run() { diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 2d0769af..56ca5ad5 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -23,275 +23,284 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2a523a5664dd5c042aa75f03c62322d7f431ab62 Mon Sep 17 00:00:00 2001 From: Calum Date: Wed, 27 Sep 2017 17:03:02 +1300 Subject: [PATCH 32/32] Added a duck boat mesh. #implement #story[1274] --- .../fxObjects/assets_3D/BoatMeshType.java | 5 +++-- .../resources/meshes/boatSTLs/ducky_eyes.stl | Bin 0 -> 3184 bytes .../resources/meshes/boatSTLs/ducky_hull.stl | Bin 0 -> 149984 bytes .../resources/meshes/boatSTLs/ducky_mast.stl | Bin 0 -> 6284 bytes .../resources/meshes/boatSTLs/ducky_sail.stl | Bin 0 -> 7684 bytes 5 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/meshes/boatSTLs/ducky_eyes.stl create mode 100644 src/main/resources/meshes/boatSTLs/ducky_hull.stl create mode 100644 src/main/resources/meshes/boatSTLs/ducky_mast.stl create mode 100644 src/main/resources/meshes/boatSTLs/ducky_sail.stl diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatMeshType.java b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatMeshType.java index 292c636a..cf01d85e 100644 --- a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatMeshType.java +++ b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatMeshType.java @@ -11,7 +11,8 @@ public enum BoatMeshType { CATAMARAN("catamaran_hull.stl", "catamaran_mast.stl", 0.997, "catamaran_sail.stl", 0.997, null, false, 1.0, 1.4, 2.0), PIRATE_SHIP("pirateship_hull.stl", "pirateship_mast.stl", -0.5415, "pirateship_mainsail.stl", - -0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2); + -0.5415, "pirateship_frontsail.stl", true, 1.2, 1.6, 1.2), + DUCKY("ducky_hull.stl", "ducky_mast.stl", -2.18539, "ducky_sail.stl", -2.18539, "ducky_eyes.stl", false, 1.2, 1.1, 1.4); final String hullFile, mastFile, sailFile, jibFile; final double mastOffset, sailOffset; @@ -19,7 +20,7 @@ public enum BoatMeshType { public final double accelerationMultiplier; public final double turnStep; final boolean fixedSail; - final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP}; + final static BoatMeshType[] boatTypes = new BoatMeshType[]{DINGHY, CATAMARAN, PIRATE_SHIP, DUCKY}; BoatMeshType(String hullFile, String mastFile, double mastOffset, String sailFile, double sailOffset, String jibFile, boolean fixedSail, double maxSpeedMultiplier, double accelerationMultiplier, double turnStep) { diff --git a/src/main/resources/meshes/boatSTLs/ducky_eyes.stl b/src/main/resources/meshes/boatSTLs/ducky_eyes.stl new file mode 100644 index 0000000000000000000000000000000000000000..841967ac41be474184d0d0185a2e2d2477d7deb0 GIT binary patch literal 3184 zcmb7GX;f8J6yD147<>*aC6_ti448n(uAer zkO^smuGDDNxz5R80urF1qB(@Xa-b54u=9lF+OL1zUvItde&7D~x6eK2cqPRyi%*P- zw2O{kw$yI&k|=#-RQ%|1P7_`2>=TxU+c`VH|9Y5wd*{F?T&G@P`Q?FPz`FT(;fRKd zqH;d7ISBn+EO>y!FT5#Gj~?q=aByHH2RNhkRl9!D#eL*_8&t}gMdg^^v2o4~K5ObV z;bZ# zw6{&e%-#pYdrAX#8GDECn!jAIGtaR3c^!8?zE^y#HoyKKAaO&g8Xr@lH0&MmW8u=L z!fksKcGXvNs!Qtu)H|+LTlO)<>eNPsB4zuZi6!x^_{5}=QzZa4(Mh#+OIKnpx*_eB zIy83WUp82M;hFXWo9B%bp(#)$fB|YZ)$L{uJLzxCpq=~WW(eKY*3Ql=vcl9S6>Th6 zr`1vKv?rIwi=w5@oEzo@aK2)(>YMc~yX0ZdC^GD1sbF&E3*^kH62QR{6ZLw*PWIXC zSxCLpo{U{xm5gl3SS1-02`xG<`u8#6hLu5;0K5(?P|ZH9V!bt~qN?t;n06ibPfZm@ zPN+kG$DhZj<4i6y=bVXxBJ+GNh;exuet1tgr%C`rbxgh2qlPVSHbt5bbwVA=4@5G? z^sHg&l0gxg8M?5&|(6pua2px<+BP(_V!lGcqE@ zwyRnm@T!tiC4gJuS>j0CQ}%22JR?^$=^3FPiN-7KovS1xB1`-%83mF-k%&WU#OMp= zujfJg1L(c@o)}UXj%x??5;RfjggOM+5;RKfm!`ptSTm%^W|uF-v=A+C_Nd@g2_SUR z1Cf_E9=F}lBF%?7p$-9dOU7!+D3A<_(A=mJK#JWSv0eRxY3}$Mtwj1o(L?}TZ#%N| zV{>s(S~sM+v|iXbjo`j!-21#?hci6-R(Ev1G8!pj-P7{ToT*X=qdN0BvrJfMG!N?P zY_l_&pn;QgI0@^HCNP>V)ur_Sx_ervTaJxFy_YFcWO%W?Xt>p#4|A{LRH?&Nbde<< zIu~G+F3rc-RoDhoHpROyM)&hUiqI5jZXGrLyfzo_eq)2~w{5XOeh2sEzEo1%4=2l& zxKZv5jScU<`3*2WWXFVJ$>@LF1}T#K*cBT)*;J{cMixg_mYRa2s_oFWs2yKA-{B$m zN1}Oj6Q-Hg@r-9qZ~g~ZpVRkr?4uM+jB{bRX5+DVtCshAr;<}$TCbxYd3P&M_?NujSWIRB-B$p44)OJ2)KMn{{Y? ze~O?8J=>`gz_m#a*bezct(IRD?U1^vZ}-8ma@RJ@2H)@oCuK0(mi&ZH8Dpo2_?Newzv_|k+J-(86)Q)tM)>xs1 z%?LH!)KaC$gt!}<{9qDc@8H$%{ zPU@2KJdq-AxrQRtKSakfwVWmb@YRYiEF9>fn0dA_>Yctt*#NUc{V{dUQN`cK3MoQ+ aqDlboKa6CWrqjCm#AKtH({!m@fPVoGe~oDX literal 0 HcmV?d00001 diff --git a/src/main/resources/meshes/boatSTLs/ducky_hull.stl b/src/main/resources/meshes/boatSTLs/ducky_hull.stl new file mode 100644 index 0000000000000000000000000000000000000000..ccd7c63d295bade9bade590309e8478e422a18f3 GIT binary patch literal 149984 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 n{7#*PdR)k-dkV)Q^ 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

o%{74<>_}g}K27W}roD zURI$eqEDtLgCQrcGcyYNxxQXRLtiykBQre}EMSIwO`rw)JbEJP@=7xJa${iXTu&%| cHTwi>R$7f=zfT3XGDGSEEqYh73Oy117Z5&gL;wH) literal 0 HcmV?d00001