From 78596ea111c5ed027eb232bffb9e6808400786f2 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 19 Sep 2017 17:47:05 +1200 Subject: [PATCH 01/42] 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/42] 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/42] 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/42] 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/42] 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 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 Date: Wed, 20 Sep 2017 17:52:02 +1200 Subject: [PATCH 06/42] 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 6ff309a40cc6486f258e59068a7628e43ff535ad Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Thu, 21 Sep 2017 14:40:35 +1200 Subject: [PATCH 07/42] - Perspective Camera Works - Top Down Camera Works - Started on chase cam but the math is a bit tricky. tags : #story[1273] --- src/main/java/seng302/model/ClientYacht.java | 11 +++ .../java/seng302/visualiser/GameView3D.java | 59 +++++++++--- .../visualiser/cameras/ChaseCamera.java | 89 +++++++++++++++++++ .../visualiser/cameras/IsometricCamera.java | 47 ++++++++++ .../visualiser/cameras/RaceCamera.java | 18 ++++ .../visualiser/cameras/TopDownCamera.java | 48 ++++++++++ 6 files changed, 258 insertions(+), 14 deletions(-) create mode 100644 src/main/java/seng302/visualiser/cameras/ChaseCamera.java create mode 100644 src/main/java/seng302/visualiser/cameras/IsometricCamera.java create mode 100644 src/main/java/seng302/visualiser/cameras/RaceCamera.java create mode 100644 src/main/java/seng302/visualiser/cameras/TopDownCamera.java diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 89715852..f14b0e5a 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -15,6 +15,7 @@ import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) @@ -56,6 +57,8 @@ public class ClientYacht extends Observable { private Integer boatStatus; private Double currentVelocity; + private BoatObject boatObject; + private List locationListeners = new ArrayList<>(); private List markRoundingListeners = new ArrayList<>(); private ReadOnlyDoubleWrapper velocityProperty = new ReadOnlyDoubleWrapper(); @@ -288,4 +291,12 @@ public class ClientYacht extends Observable { public Double getCurrentVelocity() { return currentVelocity; } + + public void setBoatObject(BoatObject newBoatObject) { + this.boatObject = newBoatObject; + } + + public BoatObject getBoatObject() { + return this.boatObject; + } } diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index c5b52286..f6f6b953 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -29,6 +29,11 @@ import seng302.model.mark.Mark; import seng302.model.token.Token; import seng302.utilities.GeoUtility; import seng302.utilities.Sounds; +import seng302.visualiser.cameras.ChaseCamera; +import seng302.visualiser.cameras.IsometricCamera; +import seng302.visualiser.cameras.RaceCamera; +import seng302.visualiser.cameras.TopDownCamera; +import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.fxObjects.MarkArrowFactory; import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.Marker3D; @@ -51,6 +56,8 @@ public class GameView3D { private SubScene view; // ParallelCamera camera; private PerspectiveCamera camera; + private PerspectiveCamera camera2; + private PerspectiveCamera camera3; private Group gameObjects; private double bufferSize = 0; @@ -87,13 +94,21 @@ public class GameView3D { } public GameView3D () { - camera = new PerspectiveCamera(true); - camera.getTransforms().addAll( - new Translate(DEFAULT_CAMERA_X,DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH) - ); + camera = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH); camera.setFarClip(600); camera.setNearClip(0.1); camera.setFieldOfView(FOV); + + camera2 = new TopDownCamera(); + camera2.setFarClip(600); + camera2.setNearClip(0.1); + camera2.setFieldOfView(FOV); + + camera3 = new ChaseCamera(); + camera3.setFarClip(600); + camera3.setNearClip(0.1); + camera3.setFieldOfView(FOV); + gameObjects = new Group(); root3D = new Group(camera, gameObjects); view = new SubScene( @@ -405,35 +420,46 @@ public class GameView3D { public void cameraMovement(KeyEvent event) { switch (event.getCode()) { case NUMPAD8: - camera.getTransforms().addAll(new Rotate(0.5, new Point3D(1,0,0))); + view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(1, 0, 0))); break; case NUMPAD2: - camera.getTransforms().addAll(new Rotate(-0.5, new Point3D(1,0,0))); + view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(1, 0, 0))); break; case NUMPAD4: - camera.getTransforms().addAll(new Rotate(-0.5, new Point3D(0,1,0))); + view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(0, 1, 0))); break; case NUMPAD6: - camera.getTransforms().addAll(new Rotate(0.5, new Point3D(0,1,0))); + view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(0, 1, 0))); break; case Z: - camera.getTransforms().addAll(new Translate(0, 0, 1.5)); + ((RaceCamera) view.getCamera()).zoomIn(); break; case X: - camera.getTransforms().addAll(new Translate(0, 0, -1.5)); + ((RaceCamera) view.getCamera()).zoomOut(); break; case W: - camera.getTransforms().addAll(new Translate(0, -1, 0)); + view.getCamera().getTransforms().addAll(new Translate(0, -1, 0)); break; case S: - camera.getTransforms().addAll(new Translate(0, 1, 0)); + view.getCamera().getTransforms().addAll(new Translate(0, 1, 0)); break; case A: - camera.getTransforms().addAll(new Translate(-1, 0, 0)); + view.getCamera().getTransforms().addAll(new Translate(-1, 0, 0)); break; case D: - camera.getTransforms().addAll(new Translate(1, 0, 0)); + view.getCamera().getTransforms().addAll(new Translate(1, 0, 0)); break; + case F1: + if (view.getCamera().equals(camera)) { + view.setCamera(camera2); + if (view.getCamera() instanceof TopDownCamera) { + ((RaceCamera) view.getCamera()).zoomIn(); + } + } else if (view.getCamera().equals(camera2)) { + view.setCamera(camera3); + } else { + view.setCamera(camera); + } } } @@ -470,6 +496,11 @@ public class GameView3D { Point2D p2d = findScaledXY(lat, lon); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); }); + + if (clientYacht.getSourceId().equals( + ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { + ((ChaseCamera) camera3).setPlayerBoat(newBoat); + } } Platform.runLater(() -> { gameObjects.getChildren().addAll(wakes); diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java new file mode 100644 index 00000000..1d0a0648 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -0,0 +1,89 @@ +package seng302.visualiser.cameras; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.geometry.Point3D; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; + + +public class ChaseCamera extends PerspectiveCamera implements RaceCamera { + + private ObservableList transforms; + private BoatObject playerBoat; + + public ChaseCamera() { + super(true); + transforms = this.getTransforms(); + } + + public void setPlayerBoat(BoatObject playerBoat) { + this.playerBoat = playerBoat; + + this.playerBoat.layoutXProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, + Number newValue) { + updateCameraX((Double) oldValue, (Double) newValue); + } + }); + this.playerBoat.layoutYProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, + Number newValue) { + updateCameraY((Double) oldValue, (Double) newValue); + } + }); + } + + + private void updateCameraX(Double oldXValue, Double newXValue) { + if (transforms.size() == 0) { // boat is placed and then moved at start, + transforms.addAll( + new Translate(playerBoat.getLayoutX() - 30, playerBoat.getLayoutY() - 30, -125), + new Rotate(80, new Point3D(0, 0, 1)) + ); + } else { + transforms.addAll(new Translate(newXValue - oldXValue, 0, 0)); + } + } + + private void updateCameraY(Double oldYValue, Double newYValue) { + transforms.addAll(new Translate(0, (newYValue - oldYValue), 0)); + } + + @Override + public void zoomIn() { + transforms.addAll(new Translate(0, 0, 1.5)); + } + + @Override + public void zoomOut() { + transforms.addAll(new Translate(0, 0, -1.5)); + } + + + /* + These have been left intentionally empty for now. it would be cool to be able to pan around the boat and have the camera move around the boat though. + */ + + @Override + public void panLeft() { + } + + @Override + public void panRight() { + } + + @Override + public void panUp() { + } + + @Override + public void panDown() { + } +} diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java new file mode 100644 index 00000000..a4abeac4 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -0,0 +1,47 @@ +package seng302.visualiser.cameras; + +import javafx.collections.ObservableList; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; + +public class IsometricCamera extends PerspectiveCamera implements RaceCamera { + + ObservableList transforms; + + public IsometricCamera(Double cameraStartX, Double cameraStartY, Double cameraDepth) { + super(true); + transforms = this.getTransforms(); + transforms.addAll(new Translate(cameraStartX, cameraStartY, cameraDepth)); + } + + @Override + public void zoomIn() { + transforms.addAll(new Translate(0, 0, 1.5)); + } + + @Override + public void zoomOut() { + transforms.addAll(new Translate(0, 0, -1.5)); + } + + @Override + public void panLeft() { + transforms.addAll(new Translate(-1, 0, 0)); + } + + @Override + public void panRight() { + transforms.addAll(new Translate(1, 0, 0)); + } + + @Override + public void panUp() { + transforms.addAll(new Translate(0, -1, 0)); + } + + @Override + public void panDown() { + transforms.addAll(new Translate(0, 1, 0)); + } +} diff --git a/src/main/java/seng302/visualiser/cameras/RaceCamera.java b/src/main/java/seng302/visualiser/cameras/RaceCamera.java new file mode 100644 index 00000000..7416ae4a --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/RaceCamera.java @@ -0,0 +1,18 @@ +package seng302.visualiser.cameras; + + +public interface RaceCamera { + + void zoomIn(); + + void zoomOut(); + + void panLeft(); + + void panRight(); + + void panUp(); + + void panDown(); + +} diff --git a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java new file mode 100644 index 00000000..624ae478 --- /dev/null +++ b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java @@ -0,0 +1,48 @@ +package seng302.visualiser.cameras; + + +import javafx.collections.ObservableList; +import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Transform; +import javafx.scene.transform.Translate; + +public class TopDownCamera extends PerspectiveCamera implements RaceCamera { + + ObservableList transforms; + + public TopDownCamera() { + super(true); + transforms = this.getTransforms(); + transforms.add(new Translate(0, 0, -125)); + } + + @Override + public void zoomIn() { + transforms.addAll(new Translate(0, 0, 1.5)); + } + + @Override + public void zoomOut() { + transforms.addAll(new Translate(0, 0, -1.5)); + } + + @Override + public void panLeft() { + transforms.addAll(new Translate(-1, 0, 0)); + } + + @Override + public void panRight() { + transforms.addAll(new Translate(1, 0, 0)); + } + + @Override + public void panUp() { + transforms.addAll(new Translate(0, -1, 0)); + } + + @Override + public void panDown() { + transforms.addAll(new Translate(0, 1, 0)); + } +} From 061e49bab96728ad2b84f91cc230ddddcff0da63 Mon Sep 17 00:00:00 2001 From: William Muir Date: Fri, 22 Sep 2017 23:44:03 +1200 Subject: [PATCH 08/42] 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 09/42] 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 10/42] 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 11/42] 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 12/42] 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 13/42] 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 14/42] 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 a19e191684a84f423c39cecf165cfd79690d5556 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Mon, 25 Sep 2017 14:03:47 +1300 Subject: [PATCH 15/42] - Chase cam kind of working, need to find a fix for the abrupt direction change. tags : #story[1273] --- .../java/seng302/visualiser/GameView3D.java | 11 +++--- .../visualiser/cameras/ChaseCamera.java | 33 +++++++--------- .../visualiser/cameras/TopDownCamera.java | 39 ++++++++++++++++++- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index f6f6b953..7c3fff69 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -438,16 +438,16 @@ public class GameView3D { ((RaceCamera) view.getCamera()).zoomOut(); break; case W: - view.getCamera().getTransforms().addAll(new Translate(0, -1, 0)); + ((RaceCamera) view.getCamera()).panUp(); break; case S: - view.getCamera().getTransforms().addAll(new Translate(0, 1, 0)); + ((RaceCamera) view.getCamera()).panDown(); break; case A: - view.getCamera().getTransforms().addAll(new Translate(-1, 0, 0)); + ((RaceCamera) view.getCamera()).panLeft(); break; case D: - view.getCamera().getTransforms().addAll(new Translate(1, 0, 0)); + ((RaceCamera) view.getCamera()).panRight(); break; case F1: if (view.getCamera().equals(camera)) { @@ -499,7 +499,8 @@ public class GameView3D { if (clientYacht.getSourceId().equals( ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { - ((ChaseCamera) camera3).setPlayerBoat(newBoat); + ((ChaseCamera) camera3).setPlayerBoat(newBoat, clientYacht); + ((TopDownCamera) camera2).setPlayerBoat(newBoat); } } Platform.runLater(() -> { diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index 1d0a0648..466e86fa 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -8,6 +8,7 @@ import javafx.scene.PerspectiveCamera; import javafx.scene.transform.Rotate; import javafx.scene.transform.Transform; import javafx.scene.transform.Translate; +import seng302.model.ClientYacht; import seng302.visualiser.fxObjects.assets_3D.BoatObject; @@ -15,45 +16,41 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { private ObservableList transforms; private BoatObject playerBoat; + private ClientYacht playerYacht; + public ChaseCamera() { super(true); transforms = this.getTransforms(); } - public void setPlayerBoat(BoatObject playerBoat) { + public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { this.playerBoat = playerBoat; - + this.playerYacht = playerYacht; this.playerBoat.layoutXProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { - updateCameraX((Double) oldValue, (Double) newValue); + repositionCamera(); } }); this.playerBoat.layoutYProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { - updateCameraY((Double) oldValue, (Double) newValue); + repositionCamera(); } }); } - - private void updateCameraX(Double oldXValue, Double newXValue) { - if (transforms.size() == 0) { // boat is placed and then moved at start, - transforms.addAll( - new Translate(playerBoat.getLayoutX() - 30, playerBoat.getLayoutY() - 30, -125), - new Rotate(80, new Point3D(0, 0, 1)) - ); - } else { - transforms.addAll(new Translate(newXValue - oldXValue, 0, 0)); - } - } - - private void updateCameraY(Double oldYValue, Double newYValue) { - transforms.addAll(new Translate(0, (newYValue - oldYValue), 0)); + private void repositionCamera() { + transforms.clear(); + transforms.addAll( + new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), + new Rotate(playerYacht.getHeading(), new Point3D(0, 0, 1)), + new Rotate(60, new Point3D(1, 0, 0)), + new Translate(0, 0, -75) + ); } @Override diff --git a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java index 624ae478..af17d553 100644 --- a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java +++ b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java @@ -1,14 +1,18 @@ package seng302.visualiser.cameras; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import javafx.scene.PerspectiveCamera; import javafx.scene.transform.Transform; import javafx.scene.transform.Translate; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; public class TopDownCamera extends PerspectiveCamera implements RaceCamera { - ObservableList transforms; + private ObservableList transforms; + private BoatObject playerBoat; public TopDownCamera() { super(true); @@ -16,6 +20,39 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { transforms.add(new Translate(0, 0, -125)); } + public void setPlayerBoat(BoatObject playerBoat) { + this.playerBoat = playerBoat; + this.playerBoat.layoutXProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, + Number newValue) { + updateCameraX((Double) oldValue, (Double) newValue); + } + }); + this.playerBoat.layoutYProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, + Number newValue) { + updateCameraY((Double) oldValue, (Double) newValue); + } + }); + } + + + private void updateCameraX(Double oldXValue, Double newXValue) { + if (transforms.size() == 0) { // boat is placed and then moved at start, + transforms.addAll( + new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), -125) + ); + } else { + transforms.addAll(new Translate(newXValue - oldXValue, 0, 0)); + } + } + + private void updateCameraY(Double oldYValue, Double newYValue) { + transforms.addAll(new Translate(0, (newYValue - oldYValue), 0)); + } + @Override public void zoomIn() { transforms.addAll(new Translate(0, 0, 1.5)); From e66abb43407c6a53bcc4788a630995b3e2372625 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Mon, 25 Sep 2017 14:39:09 +1300 Subject: [PATCH 16/42] - An attempt at chase cam smoothing. needs work. tags : #story[1273] --- src/main/java/seng302/model/ClientYacht.java | 18 +++++++++++ .../visualiser/cameras/ChaseCamera.java | 31 +++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index f14b0e5a..f2fcec96 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -65,6 +65,7 @@ public class ClientYacht extends Observable { private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper(); private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper(); private ReadOnlyIntegerWrapper placingProperty = new ReadOnlyIntegerWrapper(); + private ReadOnlyDoubleWrapper headingProperty = new ReadOnlyDoubleWrapper(); private Color colour; public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName, @@ -224,6 +225,7 @@ public class ClientYacht extends Observable { public void setHeading(Double heading) { this.heading = heading; + setHeadingProperty(); } @Override @@ -256,6 +258,7 @@ public class ClientYacht extends Observable { public void updateLocation(double lat, double lng, double heading, double velocity) { setLocation(lat, lng); this.heading = heading; + setHeadingProperty(); this.currentVelocity = velocity; updateVelocityProperty(velocity); for (YachtLocationListener yll : locationListeners) { @@ -263,6 +266,15 @@ public class ClientYacht extends Observable { } } + private void setHeadingProperty() { + Double oldHeading = getHeadingProperty().get(); + Double currHeading = heading; + while (oldHeading.equals(currHeading)) { + oldHeading++; + headingProperty.set(oldHeading); + } + } + public void addLocationListener(YachtLocationListener listener) { locationListeners.add(listener); } @@ -299,4 +311,10 @@ public class ClientYacht extends Observable { public BoatObject getBoatObject() { return this.boatObject; } + + public ReadOnlyDoubleWrapper getHeadingProperty() { + + return headingProperty; + } + } diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index 466e86fa..b84e64ec 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -17,16 +17,27 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { private ObservableList transforms; private BoatObject playerBoat; private ClientYacht playerYacht; + private Double zoomFactor; public ChaseCamera() { super(true); transforms = this.getTransforms(); + this.zoomFactor = -75.0; } public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { this.playerBoat = playerBoat; this.playerYacht = playerYacht; + + this.playerYacht.getHeadingProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, + Number newValue) { + repositionCamera(); + } + }); + this.playerBoat.layoutXProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, @@ -49,18 +60,32 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), new Rotate(playerYacht.getHeading(), new Point3D(0, 0, 1)), new Rotate(60, new Point3D(1, 0, 0)), - new Translate(0, 0, -75) + new Translate(0, 0, zoomFactor) + ); + } + + private void repositionCamera(Double newHeading) { + transforms.clear(); + transforms.addAll( + new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), + new Rotate(newHeading, new Point3D(0, 0, 1)), + new Rotate(60, new Point3D(1, 0, 0)), + new Translate(0, 0, zoomFactor) ); } @Override public void zoomIn() { - transforms.addAll(new Translate(0, 0, 1.5)); + //transforms.addAll(new Translate(0, 0, 1.5)); + this.zoomFactor += 5; + repositionCamera(); } @Override public void zoomOut() { - transforms.addAll(new Translate(0, 0, -1.5)); + //transforms.addAll(new Translate(0, 0, -1.5)); + this.zoomFactor -= 5; + repositionCamera(); } From d250c635d84e201cea65e55fb81dc266fa71015e Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Mon, 25 Sep 2017 15:32:13 +1300 Subject: [PATCH 17/42] - Adjusted server tick rate to test smoothing tags : #story[1273] --- src/main/java/seng302/model/ClientYacht.java | 11 ++++++++--- .../seng302/visualiser/ClientToServerThread.java | 2 +- .../seng302/visualiser/cameras/ChaseCamera.java | 14 +++++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index ea3058ce..3cfba60e 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -15,8 +15,8 @@ import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; +import seng302.visualiser.fxObjects.assets_3D.BoatObject; /** * Yacht class for the racing boat.

Class created to store more variables (eg. boat statuses) @@ -79,6 +79,7 @@ public class ClientYacht extends Observable { this.country = country; this.location = new GeoPoint(57.670341, 11.826856); this.heading = 120.0; //In degrees + this.headingProperty.set(this.heading); this.currentVelocity = 0d; this.boatStatus = 1; this.colour = Color.rgb(0, 0, 0, 1.0); @@ -271,7 +272,12 @@ public class ClientYacht extends Observable { Double oldHeading = getHeadingProperty().get(); Double currHeading = heading; while (oldHeading.equals(currHeading)) { - oldHeading++; + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + oldHeading += 0.5; headingProperty.set(oldHeading); } } @@ -314,7 +320,6 @@ public class ClientYacht extends Observable { } public ReadOnlyDoubleWrapper getHeadingProperty() { - return headingProperty; } diff --git a/src/main/java/seng302/visualiser/ClientToServerThread.java b/src/main/java/seng302/visualiser/ClientToServerThread.java index e53aed22..6dbcec70 100644 --- a/src/main/java/seng302/visualiser/ClientToServerThread.java +++ b/src/main/java/seng302/visualiser/ClientToServerThread.java @@ -68,7 +68,7 @@ public class ClientToServerThread implements Runnable { private Timer upWindPacketTimer = new Timer(); private Timer downWindPacketTimer = new Timer(); private boolean upwindTimerFlag = false, downwindTimerFlag = false; - static public final int PACKET_SENDING_INTERVAL_MS = 100; + static public final int PACKET_SENDING_INTERVAL_MS = 60; private int clientId = -1; diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index b84e64ec..c2496ff7 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -29,7 +29,7 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { this.playerBoat = playerBoat; this.playerYacht = playerYacht; - + System.out.println(playerYacht.getHeadingProperty().get()); this.playerYacht.getHeadingProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, @@ -74,17 +74,21 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { ); } + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < -15.0 && zoomFactor + adjustment > -125.0) { + zoomFactor = zoomFactor + adjustment; + } + } + @Override public void zoomIn() { - //transforms.addAll(new Translate(0, 0, 1.5)); - this.zoomFactor += 5; + adjustZoomFactor(5.0); repositionCamera(); } @Override public void zoomOut() { - //transforms.addAll(new Translate(0, 0, -1.5)); - this.zoomFactor -= 5; + adjustZoomFactor(-5.0); repositionCamera(); } From 00b09997b07cbd768f9b442f4d037d6f9cf8d161 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Mon, 25 Sep 2017 16:28:37 +1300 Subject: [PATCH 18/42] - Added Camera panning to chase camera mode. tags : #story[1273] --- src/main/java/seng302/model/ClientYacht.java | 16 +++------ .../visualiser/ClientToServerThread.java | 2 +- .../java/seng302/visualiser/GameView3D.java | 2 -- .../visualiser/cameras/ChaseCamera.java | 36 +++++++++++-------- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/main/java/seng302/model/ClientYacht.java b/src/main/java/seng302/model/ClientYacht.java index 3cfba60e..88d588cf 100644 --- a/src/main/java/seng302/model/ClientYacht.java +++ b/src/main/java/seng302/model/ClientYacht.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.Timer; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyIntegerProperty; @@ -58,6 +59,8 @@ public class ClientYacht extends Observable { private Integer boatStatus; private Double currentVelocity; + Timer t; + private BoatObject boatObject; private List locationListeners = new ArrayList<>(); @@ -256,7 +259,6 @@ public class ClientYacht extends Observable { this.colour = colour; } - public void updateLocation(double lat, double lng, double heading, double velocity) { setLocation(lat, lng); this.heading = heading; @@ -269,17 +271,7 @@ public class ClientYacht extends Observable { } private void setHeadingProperty() { - Double oldHeading = getHeadingProperty().get(); - Double currHeading = heading; - while (oldHeading.equals(currHeading)) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - oldHeading += 0.5; - headingProperty.set(oldHeading); - } + headingProperty.set(heading); } public void addLocationListener(YachtLocationListener listener) { diff --git a/src/main/java/seng302/visualiser/ClientToServerThread.java b/src/main/java/seng302/visualiser/ClientToServerThread.java index 6dbcec70..e53aed22 100644 --- a/src/main/java/seng302/visualiser/ClientToServerThread.java +++ b/src/main/java/seng302/visualiser/ClientToServerThread.java @@ -68,7 +68,7 @@ public class ClientToServerThread implements Runnable { private Timer upWindPacketTimer = new Timer(); private Timer downWindPacketTimer = new Timer(); private boolean upwindTimerFlag = false, downwindTimerFlag = false; - static public final int PACKET_SENDING_INTERVAL_MS = 60; + static public final int PACKET_SENDING_INTERVAL_MS = 100; private int clientId = -1; diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index db4a2758..78e43b9a 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -35,7 +35,6 @@ import seng302.visualiser.cameras.RaceCamera; import seng302.visualiser.cameras.TopDownCamera; import seng302.visualiser.controllers.ViewManager; import seng302.visualiser.fxObjects.MarkArrowFactory; -import seng302.visualiser.fxObjects.assets_3D.BoatMeshType; import seng302.visualiser.fxObjects.assets_3D.BoatObject; import seng302.visualiser.fxObjects.assets_3D.Marker3D; import seng302.visualiser.fxObjects.assets_3D.ModelFactory; @@ -47,7 +46,6 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType; public class GameView3D { - private final double FOV = 60; private final double DEFAULT_CAMERA_DEPTH = -125; private final double DEFAULT_CAMERA_X = 0; diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index c2496ff7..6c777f77 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -18,18 +18,21 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { private BoatObject playerBoat; private ClientYacht playerYacht; private Double zoomFactor; + private Double horizontalPan; + private Double verticalPan; public ChaseCamera() { super(true); transforms = this.getTransforms(); this.zoomFactor = -75.0; + this.horizontalPan = 0.0; + this.verticalPan = 0.0; } public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { this.playerBoat = playerBoat; this.playerYacht = playerYacht; - System.out.println(playerYacht.getHeadingProperty().get()); this.playerYacht.getHeadingProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, @@ -58,18 +61,9 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { transforms.clear(); transforms.addAll( new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), - new Rotate(playerYacht.getHeading(), new Point3D(0, 0, 1)), - new Rotate(60, new Point3D(1, 0, 0)), - new Translate(0, 0, zoomFactor) - ); - } - - private void repositionCamera(Double newHeading) { - transforms.clear(); - transforms.addAll( - new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), - new Rotate(newHeading, new Point3D(0, 0, 1)), - new Rotate(60, new Point3D(1, 0, 0)), + new Rotate(playerYacht.getHeadingProperty().getValue() + horizontalPan, + new Point3D(0, 0, 1)), + new Rotate(60 + verticalPan, new Point3D(1, 0, 0)), new Translate(0, 0, zoomFactor) ); } @@ -77,19 +71,25 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { private void adjustZoomFactor(Double adjustment) { if (zoomFactor + adjustment < -15.0 && zoomFactor + adjustment > -125.0) { zoomFactor = zoomFactor + adjustment; + repositionCamera(); + } + } + + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= -20 && verticalPan + adjustment <= 20) { + verticalPan += adjustment; + repositionCamera(); } } @Override public void zoomIn() { adjustZoomFactor(5.0); - repositionCamera(); } @Override public void zoomOut() { adjustZoomFactor(-5.0); - repositionCamera(); } @@ -99,17 +99,23 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { @Override public void panLeft() { + this.horizontalPan -= 5; + repositionCamera(); } @Override public void panRight() { + this.horizontalPan += 5; + repositionCamera(); } @Override public void panUp() { + adjustVerticalPan(-5.0); } @Override public void panDown() { + adjustVerticalPan(5.0); } } From 0211f2df38384c61a5e6c3967c0e74fd25d0f290 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 25 Sep 2017 17:38:09 +1300 Subject: [PATCH 19/42] 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 20/42] 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 1bd4db73cd3d30e5e19d83e44c9f4b7bf3b3994c Mon Sep 17 00:00:00 2001 From: Haoming Yin Date: Tue, 26 Sep 2017 14:44:37 +1300 Subject: [PATCH 21/42] Issue 66: client side error pop ups use default javaFx style - created JFeonix style pop up to replace the default one tags: #story[1273] --- .../java/seng302/visualiser/GameClient.java | 13 +++-- .../visualiser/controllers/ViewManager.java | 21 +++++++ .../dialogs/PopupDialogController.java | 56 +++++++++++++++++++ src/main/resources/css/dialogs/Popup.css | 33 +++++++++++ .../resources/views/dialogs/PopupDialog.fxml | 47 ++++++++++++++++ 5 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java create mode 100644 src/main/resources/css/dialogs/Popup.css create mode 100644 src/main/resources/views/dialogs/PopupDialog.fxml diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index ad3a37d6..9d64b50d 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -14,8 +14,6 @@ import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; @@ -46,6 +44,7 @@ import seng302.utilities.XMLParser; import seng302.visualiser.controllers.LobbyController; import seng302.visualiser.controllers.RaceViewController; import seng302.visualiser.controllers.ViewManager; +import seng302.visualiser.controllers.dialogs.PopupDialogController; /** * This class is a client side instance of a yacht racing game in JavaFX. The game is instantiated @@ -169,10 +168,12 @@ public class GameClient { private void showConnectionError (String message) { Platform.runLater(() -> { - Alert alert = new Alert(AlertType.ERROR); - alert.setHeaderText("Connection Error"); - alert.setContentText(message); - alert.showAndWait(); + PopupDialogController controller = ViewManager.getInstance().showPopupDialog(); + controller.setHeader("Oops"); + controller.setContent(message); + controller.setOptionButtonText("GO HOME"); + controller + .setOptionButtonEventHandler(event -> ViewManager.getInstance().goToStartView()); }); } diff --git a/src/main/java/seng302/visualiser/controllers/ViewManager.java b/src/main/java/seng302/visualiser/controllers/ViewManager.java index 8f98824c..5e3d6748 100644 --- a/src/main/java/seng302/visualiser/controllers/ViewManager.java +++ b/src/main/java/seng302/visualiser/controllers/ViewManager.java @@ -28,6 +28,7 @@ import seng302.utilities.BonjourInstallChecker; import seng302.utilities.Sounds; import seng302.visualiser.GameClient; import seng302.visualiser.controllers.dialogs.KeyBindingDialogController; +import seng302.visualiser.controllers.dialogs.PopupDialogController; public class ViewManager { @@ -229,6 +230,26 @@ public class ViewManager { keyBindingDialog.close(); } + public PopupDialogController showPopupDialog() { + FXMLLoader dialogContent = new FXMLLoader( + getClass().getResource("/views/dialogs/PopupDialog.fxml")); + for (Node node : decorator.getChildren()) { + if (node instanceof StackPane) { + try { + JFXDialog dialog = new JFXDialog((StackPane) node, dialogContent.load(), + DialogTransition.CENTER); + PopupDialogController popupDialogController = dialogContent.getController(); + popupDialogController.setPopupDialog(dialog); + dialog.show(); + return popupDialogController; + } catch (IOException e) { + logger.error("Cannot load Popup dialog"); + } + } + } + return null; + } + /** * Show a snackbar at the bottom of the app for 1 second. * diff --git a/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java b/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java new file mode 100644 index 00000000..6d294e50 --- /dev/null +++ b/src/main/java/seng302/visualiser/controllers/dialogs/PopupDialogController.java @@ -0,0 +1,56 @@ +package seng302.visualiser.controllers.dialogs; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXDialog; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; + +public class PopupDialogController implements Initializable { + + @FXML + private Label headerLabel; + @FXML + private Label contentLabel; + @FXML + private Label closeLabel; + @FXML + private JFXButton optionButton; + + @FXML + private JFXDialog popupDialog; + + @Override + public void initialize(URL location, ResourceBundle resources) { + + } + + public void setContent(String content) { + this.contentLabel.setText(content); + } + + public void setHeader(String header) { + this.headerLabel.setText(header); + } + + public void setOptionButton(JFXButton jfxButton) { + this.optionButton = jfxButton; + } + + public void setOptionButtonText(String text) { + this.optionButton.setText(text); + } + + public void setOptionButtonEventHandler(EventHandler eventHandler) { + this.optionButton.setOnMouseClicked(eventHandler); + } + + public void setPopupDialog(JFXDialog popupDialog) { + this.popupDialog = popupDialog; + this.closeLabel.setOnMouseClicked(event -> this.popupDialog.close()); + } +} diff --git a/src/main/resources/css/dialogs/Popup.css b/src/main/resources/css/dialogs/Popup.css new file mode 100644 index 00000000..d0dd364b --- /dev/null +++ b/src/main/resources/css/dialogs/Popup.css @@ -0,0 +1,33 @@ +#headerLabel { + -fx-font-size: 20px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#closeLabel { + -fx-font-size: 22px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#closeLabel:hover { + -fx-font-size: 24px; + -fx-text-fill: red; +} + +#contentLabel { + -fx-font-size: 22px; + -fx-text-fill: -fx-pp-dark-text-color; +} + +#optionButton { + -fx-background-color: -fx-pp-theme-color; + -fx-text-fill: -fx-pp-light-text-color; + -fx-font-size: 18px; + -fx-effect: -fx-pp-dropshadow-light; + -fx-max-height: 55; + -fx-focus-traversable: false; +} + +#optionButton:hover { + -fx-font-size: 20px !important; + -fx-background-color: -fx-pp-light-theme-color; +} \ No newline at end of file diff --git a/src/main/resources/views/dialogs/PopupDialog.fxml b/src/main/resources/views/dialogs/PopupDialog.fxml new file mode 100644 index 00000000..00574bc4 --- /dev/null +++ b/src/main/resources/views/dialogs/PopupDialog.fxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2e7487fdfcff2de498a66aa746dad68378bb2dec Mon Sep 17 00:00:00 2001 From: alistairjmcintyre Date: Tue, 26 Sep 2017 15:06:21 +1300 Subject: [PATCH 22/42] - Chase Camera: - Has panning bounds, zoom bounds, and general tidy up. - Now correctly observes the boat object rather than getting information from both the boat object AND the client yacht model. Top Down Camera: - Can only pan within certain bounds now, and will continue to follow the boat regardless. - Can only zoom within certain bounds now. Isometric Camera: - Nothing changed. #tags [1273] --- .../java/seng302/visualiser/GameView3D.java | 68 ++++++++------- .../visualiser/cameras/ChaseCamera.java | 66 ++++++-------- .../visualiser/cameras/IsometricCamera.java | 7 ++ .../visualiser/cameras/TopDownCamera.java | 86 +++++++++++-------- .../fxObjects/assets_3D/BoatObject.java | 9 ++ 5 files changed, 130 insertions(+), 106 deletions(-) diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index 78e43b9a..5094e41c 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -1,6 +1,7 @@ package seng302.visualiser; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -9,6 +10,7 @@ import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.geometry.Point2D; import javafx.geometry.Point3D; +import javafx.scene.Camera; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.PerspectiveCamera; @@ -53,12 +55,13 @@ public class GameView3D { private Group root3D; private SubScene view; - // ParallelCamera camera; - private PerspectiveCamera camera; - private PerspectiveCamera camera2; - private PerspectiveCamera camera3; private Group gameObjects; + // Cameras + private PerspectiveCamera isometricCam; + private PerspectiveCamera topDownCam; + private PerspectiveCamera chaseCam; + private double bufferSize = 0; private double canvasWidth = 200; private double canvasHeight = 200; @@ -93,28 +96,25 @@ public class GameView3D { } public GameView3D () { - camera = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, DEFAULT_CAMERA_DEPTH); - camera.setFarClip(600); - camera.setNearClip(0.1); - camera.setFieldOfView(FOV); + isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, + DEFAULT_CAMERA_DEPTH); + topDownCam = new TopDownCamera(); + chaseCam = new ChaseCamera(); - camera2 = new TopDownCamera(); - camera2.setFarClip(600); - camera2.setNearClip(0.1); - camera2.setFieldOfView(FOV); - - camera3 = new ChaseCamera(); - camera3.setFarClip(600); - camera3.setNearClip(0.1); - camera3.setFieldOfView(FOV); + for (PerspectiveCamera pc : Arrays.asList(isometricCam, topDownCam, chaseCam)) { + pc.setFarClip(600); + pc.setNearClip(0.1); + pc.setFieldOfView(FOV); + } gameObjects = new Group(); - root3D = new Group(camera, gameObjects); + root3D = new Group(isometricCam, gameObjects); view = new SubScene( root3D, 1000, 1000, true, SceneAntialiasing.BALANCED ); - view.setCamera(camera); - camera.getTransforms().add(new Rotate(30, new Point3D(1,0,0))); + view.setCamera(isometricCam); + isometricCam.getTransforms() + .add(new Rotate(30, new Point3D(1, 0, 0))); //todo: move this into isometric cam? gameObjects.getChildren().addAll( ModelFactory.importModel(ModelType.OCEAN).getAssets(), @@ -449,16 +449,20 @@ public class GameView3D { ((RaceCamera) view.getCamera()).panRight(); break; case F1: - if (view.getCamera().equals(camera)) { - view.setCamera(camera2); - if (view.getCamera() instanceof TopDownCamera) { - ((RaceCamera) view.getCamera()).zoomIn(); - } - } else if (view.getCamera().equals(camera2)) { - view.setCamera(camera3); - } else { - view.setCamera(camera); - } + toggleCamera(); + break; + } + } + + private void toggleCamera() { + Camera currCamera = view.getCamera(); + + if (currCamera.equals(isometricCam)) { + view.setCamera(topDownCam); + } else if (currCamera.equals(topDownCam)) { + view.setCamera(chaseCam); + } else { + view.setCamera(isometricCam); } } @@ -498,8 +502,8 @@ public class GameView3D { if (clientYacht.getSourceId().equals( ViewManager.getInstance().getGameClient().getServerThread().getClientId())) { - ((ChaseCamera) camera3).setPlayerBoat(newBoat, clientYacht); - ((TopDownCamera) camera2).setPlayerBoat(newBoat); + ((ChaseCamera) chaseCam).setPlayerBoat(newBoat); + ((TopDownCamera) topDownCam).setPlayerBoat(newBoat); } } Platform.runLater(() -> { diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index 6c777f77..daddfaca 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -1,22 +1,25 @@ package seng302.visualiser.cameras; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; +import java.util.Arrays; +import javafx.beans.property.DoubleProperty; import javafx.collections.ObservableList; import javafx.geometry.Point3D; import javafx.scene.PerspectiveCamera; import javafx.scene.transform.Rotate; import javafx.scene.transform.Transform; import javafx.scene.transform.Translate; -import seng302.model.ClientYacht; import seng302.visualiser.fxObjects.assets_3D.BoatObject; public class ChaseCamera extends PerspectiveCamera implements RaceCamera { + private final Double VERTICAL_PAN_LIMIT = 20.0; + private final Double NEAR_ZOOM_LIMIT = -15.0; + private final Double FAR_ZOOM_LIMIT = -125.0; + private ObservableList transforms; private BoatObject playerBoat; - private ClientYacht playerYacht; + private Double zoomFactor; private Double horizontalPan; private Double verticalPan; @@ -25,43 +28,27 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { public ChaseCamera() { super(true); transforms = this.getTransforms(); - this.zoomFactor = -75.0; + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; this.horizontalPan = 0.0; this.verticalPan = 0.0; } - public void setPlayerBoat(BoatObject playerBoat, ClientYacht playerYacht) { + public void setPlayerBoat(BoatObject playerBoat) { this.playerBoat = playerBoat; - this.playerYacht = playerYacht; - this.playerYacht.getHeadingProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, - Number newValue) { - repositionCamera(); - } - }); - this.playerBoat.layoutXProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, - Number newValue) { - repositionCamera(); - } - }); - this.playerBoat.layoutYProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, - Number newValue) { - repositionCamera(); - } - }); + for (DoubleProperty o : Arrays + .asList(playerBoat.getRotationProperty(), playerBoat.layoutYProperty(), + playerBoat.layoutXProperty())) { + o.addListener((obs, oldVal, newVal) -> repositionCamera()); + } } private void repositionCamera() { transforms.clear(); transforms.addAll( new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), 0), - new Rotate(playerYacht.getHeadingProperty().getValue() + horizontalPan, + new Rotate(playerBoat.getRotationProperty().getValue() + horizontalPan, new Point3D(0, 0, 1)), new Rotate(60 + verticalPan, new Point3D(1, 0, 0)), new Translate(0, 0, zoomFactor) @@ -69,19 +56,25 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { } private void adjustZoomFactor(Double adjustment) { - if (zoomFactor + adjustment < -15.0 && zoomFactor + adjustment > -125.0) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { zoomFactor = zoomFactor + adjustment; repositionCamera(); } } private void adjustVerticalPan(Double adjustment) { - if (verticalPan + adjustment >= -20 && verticalPan + adjustment <= 20) { + if (verticalPan + adjustment >= -VERTICAL_PAN_LIMIT + && verticalPan + adjustment <= VERTICAL_PAN_LIMIT) { verticalPan += adjustment; repositionCamera(); } } + private void adjustHorizontalPan(Double adjustment) { + this.horizontalPan += adjustment; + repositionCamera(); + } + @Override public void zoomIn() { adjustZoomFactor(5.0); @@ -92,21 +85,14 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { adjustZoomFactor(-5.0); } - - /* - These have been left intentionally empty for now. it would be cool to be able to pan around the boat and have the camera move around the boat though. - */ - @Override public void panLeft() { - this.horizontalPan -= 5; - repositionCamera(); + adjustHorizontalPan(-5.0); } @Override public void panRight() { - this.horizontalPan += 5; - repositionCamera(); + adjustHorizontalPan(5.0); } @Override diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java index a4abeac4..434032b7 100644 --- a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -7,6 +7,13 @@ import javafx.scene.transform.Translate; public class IsometricCamera extends PerspectiveCamera implements RaceCamera { + private final Double PAN_LIMIT = 50.0; + private final Double NEAR_ZOOM_LIMIT = -15.0; + private final Double FAR_ZOOM_LIMIT = -125.0; + + private Double horizontalAdjustment; + private Double verticalAdjustment; + ObservableList transforms; public IsometricCamera(Double cameraStartX, Double cameraStartY, Double cameraDepth) { diff --git a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java index af17d553..09455345 100644 --- a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java +++ b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java @@ -1,8 +1,8 @@ package seng302.visualiser.cameras; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; +import java.util.Arrays; +import javafx.beans.property.DoubleProperty; import javafx.collections.ObservableList; import javafx.scene.PerspectiveCamera; import javafx.scene.transform.Transform; @@ -11,75 +11,93 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject; public class TopDownCamera extends PerspectiveCamera implements RaceCamera { + private final Double PAN_LIMIT = 30.0; + private final Double NEAR_ZOOM_LIMIT = -30.0; + private final Double FAR_ZOOM_LIMIT = -130.0; + private final Double ZOOM_STEP = 2.5; + private ObservableList transforms; private BoatObject playerBoat; + private Double zoomFactor; + private Double horizontalPan; + private Double verticalPan; + public TopDownCamera() { super(true); transforms = this.getTransforms(); - transforms.add(new Translate(0, 0, -125)); + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; + horizontalPan = 0.0; + verticalPan = 0.0; } public void setPlayerBoat(BoatObject playerBoat) { this.playerBoat = playerBoat; - this.playerBoat.layoutXProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, - Number newValue) { - updateCameraX((Double) oldValue, (Double) newValue); - } - }); - this.playerBoat.layoutYProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Number oldValue, - Number newValue) { - updateCameraY((Double) oldValue, (Double) newValue); - } - }); - } - - private void updateCameraX(Double oldXValue, Double newXValue) { - if (transforms.size() == 0) { // boat is placed and then moved at start, - transforms.addAll( - new Translate(playerBoat.getLayoutX(), playerBoat.getLayoutY(), -125) - ); - } else { - transforms.addAll(new Translate(newXValue - oldXValue, 0, 0)); + for (DoubleProperty o : Arrays + .asList(playerBoat.layoutXProperty(), playerBoat.layoutYProperty())) { + o.addListener((obs, oldVal, newVal) -> updateCamera()); } } - private void updateCameraY(Double oldYValue, Double newYValue) { - transforms.addAll(new Translate(0, (newYValue - oldYValue), 0)); + private void updateCamera() { + transforms.clear(); + transforms.addAll( + new Translate(playerBoat.getLayoutX() + horizontalPan, + playerBoat.getLayoutY() + verticalPan, zoomFactor) + ); + } + + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { + zoomFactor = zoomFactor + adjustment; + updateCamera(); + } + } + + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= -PAN_LIMIT && verticalPan + adjustment <= PAN_LIMIT) { + verticalPan += adjustment; + updateCamera(); + } + } + + private void adjustHorizontalPan(Double adjustment) { + if (horizontalPan + adjustment >= -PAN_LIMIT && horizontalPan + adjustment <= PAN_LIMIT) { + horizontalPan += adjustment; + updateCamera(); + } } @Override public void zoomIn() { - transforms.addAll(new Translate(0, 0, 1.5)); + adjustZoomFactor(ZOOM_STEP); } @Override public void zoomOut() { - transforms.addAll(new Translate(0, 0, -1.5)); + adjustZoomFactor(-ZOOM_STEP); } @Override public void panLeft() { - transforms.addAll(new Translate(-1, 0, 0)); + adjustHorizontalPan(-1.0); } @Override public void panRight() { - transforms.addAll(new Translate(1, 0, 0)); + adjustHorizontalPan(1.0); } @Override public void panUp() { - transforms.addAll(new Translate(0, -1, 0)); + adjustVerticalPan(-1.0); } @Override public void panDown() { - transforms.addAll(new Translate(0, 1, 0)); + adjustVerticalPan(1.0); } + } diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java index abf969a8..025ab820 100644 --- a/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java +++ b/src/main/java/seng302/visualiser/fxObjects/assets_3D/BoatObject.java @@ -3,6 +3,7 @@ package seng302.visualiser.fxObjects.assets_3D; import java.util.ArrayList; import java.util.List; import javafx.application.Platform; +import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.geometry.Point3D; import javafx.scene.Group; import javafx.scene.paint.Color; @@ -30,12 +31,15 @@ public class BoatObject extends Group { private Boolean isSelected = false; private Rotate rotation = new Rotate(0, new Point3D(0,0,1)); + private ReadOnlyDoubleWrapper rotationProperty; + private List selectedBoatListenerListeners = new ArrayList<>(); /** * Creates a BoatGroup with the default triangular boat polygon. */ public BoatObject(BoatMeshType boatMeshType) { + rotationProperty = new ReadOnlyDoubleWrapper(0.0); boatAssets = ModelFactory.boatGameView(boatMeshType, colour); boatAssets.hideSail(); boatAssets.getAssets().getTransforms().addAll( @@ -83,6 +87,7 @@ public class BoatObject extends Group { private void rotateTo(double heading, boolean sailsIn, double windDir) { + rotationProperty.set(heading); rotation.setAngle(heading); wake.getTransforms().setAll(new Rotate(heading, new Point3D(0,0,1))); if (sailsIn) { @@ -130,4 +135,8 @@ public class BoatObject extends Group { public void addSelectedBoatListener(SelectedBoatListener sbl) { selectedBoatListenerListeners.add(sbl); } + + public ReadOnlyDoubleWrapper getRotationProperty() { + return rotationProperty; + } } \ No newline at end of file From 132a729758caf242464b2d9c684a879497040150 Mon Sep 17 00:00:00 2001 From: Haoming Yin Date: Tue, 26 Sep 2017 15:49:16 +1300 Subject: [PATCH 23/42] Temporarily added more options in key binding dialog. But still need to implement binding after camera story has been merged into develop tags: #story[1273] --- src/main/java/seng302/model/GameKeyBind.java | 7 +- src/main/java/seng302/model/KeyAction.java | 7 +- .../dialogs/KeyBindingDialogController.java | 16 +- src/main/resources/css/Master.css | 5 + .../css/dialogs/KeyBindingDialog.css | 9 +- .../views/dialogs/KeyBindingDialog.fxml | 293 ++++++++++++------ 6 files changed, 239 insertions(+), 98 deletions(-) diff --git a/src/main/java/seng302/model/GameKeyBind.java b/src/main/java/seng302/model/GameKeyBind.java index 1c765adc..c25f6202 100644 --- a/src/main/java/seng302/model/GameKeyBind.java +++ b/src/main/java/seng302/model/GameKeyBind.java @@ -30,7 +30,12 @@ public class GameKeyBind { keys.add(KeyCode.ENTER); keys.add(KeyCode.PAGE_UP); keys.add(KeyCode.PAGE_DOWN); - for (int i = 0; i < 7; i++) { + keys.add(KeyCode.F1); + keys.add(KeyCode.D); + keys.add(KeyCode.A); + keys.add(KeyCode.W); + keys.add(KeyCode.S); + for (int i = 0; i < 12; i++) { actionToKeyMap.put(KeyAction.getType(i + 1), keys.get(i)); keyToActionMap.put(keys.get(i), KeyAction.getType(i + 1)); } diff --git a/src/main/java/seng302/model/KeyAction.java b/src/main/java/seng302/model/KeyAction.java index 1b8c2fa1..51509027 100644 --- a/src/main/java/seng302/model/KeyAction.java +++ b/src/main/java/seng302/model/KeyAction.java @@ -10,7 +10,12 @@ public enum KeyAction { SAILS_STATE(4), TACK_GYBE(5), UPWIND(6), - DOWNWIND(7); + DOWNWIND(7), + VIEW(8), + RIGHT(9), + LEFT(10), + FORWARD(11), + BACKWARD(12); private final int type; private static final Map intToTypeMap = new HashMap<>(); diff --git a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java index 2a286ec4..c650bc59 100644 --- a/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java +++ b/src/main/java/seng302/visualiser/controllers/dialogs/KeyBindingDialogController.java @@ -48,6 +48,16 @@ public class KeyBindingDialogController implements Initializable { private Label downwindLabel; @FXML private JFXToggleButton turningToggle; + @FXML + private JFXButton viewButton; + @FXML + private JFXButton rightButton; + @FXML + private JFXButton leftButton; + @FXML + private JFXButton forwardButton; + @FXML + private JFXButton backwardButton; //---------FXML END---------// private GameKeyBind gameKeyBind; @@ -60,7 +70,9 @@ public class KeyBindingDialogController implements Initializable { gameKeyBind = GameKeyBind.getInstance(); buttons = new ArrayList<>(); Collections.addAll(buttons, - zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn); + zoomInbtn, zoomOutBtn, vmgBtn, sailInOutBtn, tackGybeBtn, upwindBtn, downwindBtn, + viewButton, + rightButton, leftButton, forwardButton, backwardButton); bindButtonWithAction(); loadKeyBind(); @@ -106,7 +118,7 @@ public class KeyBindingDialogController implements Initializable { */ private void bindButtonWithAction() { buttonActionMap = new HashMap<>(); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < 12; i++) { buttonActionMap.put(buttons.get(i), KeyAction.getType(i + 1)); } } diff --git a/src/main/resources/css/Master.css b/src/main/resources/css/Master.css index cb50a645..478d7cf0 100644 --- a/src/main/resources/css/Master.css +++ b/src/main/resources/css/Master.css @@ -51,6 +51,11 @@ /********* customised scroll bar for scroll pane ***********/ +.scroll-pane { + -fx-focus-traversable: false; + -fx-border-style: none; +} + /* The main scrollbar **track** CSS class */ .scroll-bar:horizontal .track, .scroll-bar:vertical .track { diff --git a/src/main/resources/css/dialogs/KeyBindingDialog.css b/src/main/resources/css/dialogs/KeyBindingDialog.css index 8d09e130..6c7bff26 100644 --- a/src/main/resources/css/dialogs/KeyBindingDialog.css +++ b/src/main/resources/css/dialogs/KeyBindingDialog.css @@ -9,8 +9,13 @@ } #closeLabel:hover { - -fx-text-fill: -fx-pp-theme-color; - -fx-font-size: 33; + -fx-text-fill: red; + -fx-font-size: 33px; +} + +.sectionLabel { + -fx-text-fill: -fx-pp-dark-text-color; + -fx-font-size: 20px; } JFXButton { diff --git a/src/main/resources/views/dialogs/KeyBindingDialog.fxml b/src/main/resources/views/dialogs/KeyBindingDialog.fxml index 4a677797..7fcf2e7f 100644 --- a/src/main/resources/views/dialogs/KeyBindingDialog.fxml +++ b/src/main/resources/views/dialogs/KeyBindingDialog.fxml @@ -6,117 +6,226 @@ + + + + + + - - + - - - - - - - - - - + + - - - - - From 1a53579317af7868e6cd806bff3ac2478e6233d3 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Tue, 26 Sep 2017 16:57:07 +1300 Subject: [PATCH 24/42] - Isometric Camera - Zoom Bounds added. - Camera Panning Bounds added. Need to be tested with extra maps. tags : #story[1273] --- .../java/seng302/visualiser/GameView3D.java | 6 +- .../visualiser/cameras/IsometricCamera.java | 75 +++++++++++++++---- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index 5094e41c..a7633b3f 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -49,7 +49,6 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType; public class GameView3D { private final double FOV = 60; - private final double DEFAULT_CAMERA_DEPTH = -125; private final double DEFAULT_CAMERA_X = 0; private final double DEFAULT_CAMERA_Y = 155; @@ -96,8 +95,7 @@ public class GameView3D { } public GameView3D () { - isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y, - DEFAULT_CAMERA_DEPTH); + isometricCam = new IsometricCamera(DEFAULT_CAMERA_X, DEFAULT_CAMERA_Y); topDownCam = new TopDownCamera(); chaseCam = new ChaseCamera(); @@ -113,8 +111,6 @@ public class GameView3D { root3D, 1000, 1000, true, SceneAntialiasing.BALANCED ); view.setCamera(isometricCam); - isometricCam.getTransforms() - .add(new Rotate(30, new Point3D(1, 0, 0))); //todo: move this into isometric cam? gameObjects.getChildren().addAll( ModelFactory.importModel(ModelType.OCEAN).getAssets(), diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java index 434032b7..9814079c 100644 --- a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -1,54 +1,101 @@ package seng302.visualiser.cameras; import javafx.collections.ObservableList; +import javafx.geometry.Point3D; import javafx.scene.PerspectiveCamera; +import javafx.scene.transform.Rotate; import javafx.scene.transform.Transform; import javafx.scene.transform.Translate; public class IsometricCamera extends PerspectiveCamera implements RaceCamera { - private final Double PAN_LIMIT = 50.0; - private final Double NEAR_ZOOM_LIMIT = -15.0; - private final Double FAR_ZOOM_LIMIT = -125.0; + private final Double MIN_X = -120.0; + private final Double MAX_X = 125.0; - private Double horizontalAdjustment; - private Double verticalAdjustment; + private final Double MIN_Y = 40.0; + private final Double MAX_Y = 170.0; - ObservableList transforms; + private final Double PAN_LIMIT = 160.0; + private final Double NEAR_ZOOM_LIMIT = -50.0; + private final Double FAR_ZOOM_LIMIT = -160.0; - public IsometricCamera(Double cameraStartX, Double cameraStartY, Double cameraDepth) { + private Double horizontalPan; + private Double verticalPan; + private Double zoomFactor; + + private ObservableList transforms; + + public IsometricCamera(Double cameraStartX, Double cameraStartY) { super(true); transforms = this.getTransforms(); - transforms.addAll(new Translate(cameraStartX, cameraStartY, cameraDepth)); + + zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0; + horizontalPan = cameraStartX; + verticalPan = cameraStartY; + + updateCamera(); + } + + private void updateCamera() { + System.out.println("----"); + System.out.println(horizontalPan); + System.out.println(verticalPan); + System.out.println("----"); + transforms.clear(); + transforms.addAll( + new Translate(horizontalPan, verticalPan, zoomFactor), + new Rotate(30, new Point3D(1, 0, 0)) + ); + } + + private void adjustZoomFactor(Double adjustment) { + if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { + zoomFactor = zoomFactor + adjustment; + updateCamera(); + } + } + + private void adjustVerticalPan(Double adjustment) { + if (verticalPan + adjustment >= MIN_Y && verticalPan + adjustment <= MAX_Y) { + verticalPan += adjustment; + updateCamera(); + } + } + + private void adjustHorizontalPan(Double adjustment) { + if (horizontalPan + adjustment >= MIN_X && horizontalPan + adjustment <= MIN_Y) { + this.horizontalPan += adjustment; + updateCamera(); + } } @Override public void zoomIn() { - transforms.addAll(new Translate(0, 0, 1.5)); + adjustZoomFactor(-2.5); } @Override public void zoomOut() { - transforms.addAll(new Translate(0, 0, -1.5)); + adjustZoomFactor(2.5); } @Override public void panLeft() { - transforms.addAll(new Translate(-1, 0, 0)); + adjustHorizontalPan(-2.5); } @Override public void panRight() { - transforms.addAll(new Translate(1, 0, 0)); + adjustHorizontalPan(2.5); } @Override public void panUp() { - transforms.addAll(new Translate(0, -1, 0)); + adjustVerticalPan(-2.5); } @Override public void panDown() { - transforms.addAll(new Translate(0, 1, 0)); + adjustVerticalPan(2.5); } } From dd43097677a37f546706273ff48e17bafab91c13 Mon Sep 17 00:00:00 2001 From: Peter Galloway Date: Tue, 26 Sep 2017 17:07:55 +1300 Subject: [PATCH 25/42] the boat can now turn after snapping to starboard vmg #story[1273] --- src/main/java/seng302/model/ServerYacht.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/seng302/model/ServerYacht.java b/src/main/java/seng302/model/ServerYacht.java index 5822b248..f7cdaeba 100644 --- a/src/main/java/seng302/model/ServerYacht.java +++ b/src/main/java/seng302/model/ServerYacht.java @@ -156,11 +156,11 @@ public class ServerYacht { /** * Enables the boats auto pilot feature, which will move the boat towards a given heading. * - * @param thisHeading The heading to move the boat towards. + * @param newHeading The heading to move the boat towards. */ - private void setAutoPilot(Double thisHeading) { + private void setAutoPilot(Double newHeading) { isAuto = true; - autoHeading = thisHeading; + autoHeading = newHeading; } /** @@ -178,8 +178,9 @@ public class ServerYacht { if (isAuto) { turnTowardsHeading(autoHeading); if (Math.abs(heading - autoHeading) - <= turnStep) { //Cancel when within 1 turn step of target. + <= turnStep*1.5) { isAuto = false; + setHeading(autoHeading); } } } @@ -265,7 +266,7 @@ public class ServerYacht { // Take optimal heading and turn into a boat heading rather than a wind heading. optimalHeading = - optimalHeading + GameState.getWindDirection(); + (optimalHeading + GameState.getWindDirection()) % 360; setAutoPilot(optimalHeading); } From 8ba44d7476a718a964a56d1ea577861333a29b34 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 17:19:45 +1300 Subject: [PATCH 26/42] 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 66e6a8a2a4fce06ffcd4f24df8c8d774ec62a00e Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Tue, 26 Sep 2017 17:35:36 +1300 Subject: [PATCH 27/42] - Small Changes tags : #story[1273] --- .../seng302/visualiser/cameras/ChaseCamera.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index daddfaca..fddd1810 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -17,6 +17,9 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { private final Double NEAR_ZOOM_LIMIT = -15.0; private final Double FAR_ZOOM_LIMIT = -125.0; + private final Double ZOOM_STEP = 2.5; + private final Double PAN_STEP = 2.5; + private ObservableList transforms; private BoatObject playerBoat; @@ -77,31 +80,31 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { @Override public void zoomIn() { - adjustZoomFactor(5.0); + adjustZoomFactor(ZOOM_STEP); } @Override public void zoomOut() { - adjustZoomFactor(-5.0); + adjustZoomFactor(-ZOOM_STEP); } @Override public void panLeft() { - adjustHorizontalPan(-5.0); + adjustHorizontalPan(-PAN_STEP); } @Override public void panRight() { - adjustHorizontalPan(5.0); + adjustHorizontalPan(PAN_STEP); } @Override public void panUp() { - adjustVerticalPan(-5.0); + adjustVerticalPan(-PAN_STEP); } @Override public void panDown() { - adjustVerticalPan(5.0); + adjustVerticalPan(PAN_STEP); } } From 99ce4fa11d3b3a289d2911b22f00db08d26c1aab Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Tue, 26 Sep 2017 18:01:39 +1300 Subject: [PATCH 28/42] Merged develop in and reimplemented keybindings for moving the camera. tags : #story[1273] --- src/main/java/seng302/model/GameKeyBind.java | 4 +++ .../java/seng302/visualiser/GameView3D.java | 35 ++++++++----------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/seng302/model/GameKeyBind.java b/src/main/java/seng302/model/GameKeyBind.java index c25f6202..5627625c 100644 --- a/src/main/java/seng302/model/GameKeyBind.java +++ b/src/main/java/seng302/model/GameKeyBind.java @@ -52,6 +52,10 @@ public class GameKeyBind { return instance.actionToKeyMap.get(keyAction); } + public KeyAction getKeyAction(KeyCode keyCode) { + return instance.keyToActionMap.get(keyCode); + } + /** * Binds a key to a key action * diff --git a/src/main/java/seng302/visualiser/GameView3D.java b/src/main/java/seng302/visualiser/GameView3D.java index a7633b3f..8a944c20 100644 --- a/src/main/java/seng302/visualiser/GameView3D.java +++ b/src/main/java/seng302/visualiser/GameView3D.java @@ -23,7 +23,9 @@ import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; import seng302.gameServer.messages.RoundingSide; import seng302.model.ClientYacht; +import seng302.model.GameKeyBind; import seng302.model.GeoPoint; +import seng302.model.KeyAction; import seng302.model.Limit; import seng302.model.mark.CompoundMark; import seng302.model.mark.Corner; @@ -121,6 +123,8 @@ public class GameView3D { scene.addEventHandler(KeyEvent.KEY_PRESSED, this::cameraMovement); } }); + + } public void updateCourse(List newCourse, List sequence) { @@ -345,7 +349,6 @@ public class GameView3D { * it to distanceScaleFactor Returns the max horizontal distance of the map. */ private double scaleRaceExtremities() { - double vertAngle = Math.abs( GeoUtility.getBearingRad(minLatPoint, maxLatPoint) ); @@ -413,38 +416,28 @@ public class GameView3D { } public void cameraMovement(KeyEvent event) { - switch (event.getCode()) { - case NUMPAD8: - view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(1, 0, 0))); - break; - case NUMPAD2: - view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(1, 0, 0))); - break; - case NUMPAD4: - view.getCamera().getTransforms().addAll(new Rotate(-0.5, new Point3D(0, 1, 0))); - break; - case NUMPAD6: - view.getCamera().getTransforms().addAll(new Rotate(0.5, new Point3D(0, 1, 0))); - break; - case Z: + GameKeyBind keyBinds = GameKeyBind.getInstance(); + KeyAction keyPressed = keyBinds.getKeyAction(event.getCode()); + switch (keyPressed) { + case ZOOM_IN: ((RaceCamera) view.getCamera()).zoomIn(); break; - case X: + case ZOOM_OUT: ((RaceCamera) view.getCamera()).zoomOut(); break; - case W: + case FORWARD: ((RaceCamera) view.getCamera()).panUp(); break; - case S: + case BACKWARD: ((RaceCamera) view.getCamera()).panDown(); break; - case A: + case LEFT: ((RaceCamera) view.getCamera()).panLeft(); break; - case D: + case RIGHT: ((RaceCamera) view.getCamera()).panRight(); break; - case F1: + case VIEW: toggleCamera(); break; } From f11c457d28218f7159fadf96b2cc70905b3570e0 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Tue, 26 Sep 2017 18:31:47 +1300 Subject: [PATCH 29/42] removed print statements tags : #story[1273] --- src/main/java/seng302/visualiser/cameras/IsometricCamera.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java index 9814079c..41a1d563 100644 --- a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -37,10 +37,6 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera { } private void updateCamera() { - System.out.println("----"); - System.out.println(horizontalPan); - System.out.println(verticalPan); - System.out.println("----"); transforms.clear(); transforms.addAll( new Translate(horizontalPan, verticalPan, zoomFactor), From 9b00f769072009e2470c5e9b9bec61ffffcf5368 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Tue, 26 Sep 2017 18:36:13 +1300 Subject: [PATCH 30/42] removed print statements and added documentation. tags : #story[1273] --- .../visualiser/cameras/ChaseCamera.java | 20 +++++++++++++++++++ .../visualiser/cameras/IsometricCamera.java | 16 +++++++++++++++ .../visualiser/cameras/TopDownCamera.java | 20 +++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java index fddd1810..0647a473 100644 --- a/src/main/java/seng302/visualiser/cameras/ChaseCamera.java +++ b/src/main/java/seng302/visualiser/cameras/ChaseCamera.java @@ -37,6 +37,11 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { this.verticalPan = 0.0; } + /** + * Sets a player boat object to observe and update the camera with. + * + * @param playerBoat The player boat to be observed. + */ public void setPlayerBoat(BoatObject playerBoat) { this.playerBoat = playerBoat; @@ -47,6 +52,9 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ private void repositionCamera() { transforms.clear(); transforms.addAll( @@ -58,6 +66,10 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { ); } + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * @param adjustment the adjustment to be made to the camera + */ private void adjustZoomFactor(Double adjustment) { if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { zoomFactor = zoomFactor + adjustment; @@ -65,6 +77,10 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ private void adjustVerticalPan(Double adjustment) { if (verticalPan + adjustment >= -VERTICAL_PAN_LIMIT && verticalPan + adjustment <= VERTICAL_PAN_LIMIT) { @@ -73,6 +89,10 @@ public class ChaseCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ private void adjustHorizontalPan(Double adjustment) { this.horizontalPan += adjustment; repositionCamera(); diff --git a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java index 41a1d563..85a0e502 100644 --- a/src/main/java/seng302/visualiser/cameras/IsometricCamera.java +++ b/src/main/java/seng302/visualiser/cameras/IsometricCamera.java @@ -36,6 +36,9 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera { updateCamera(); } + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ private void updateCamera() { transforms.clear(); transforms.addAll( @@ -44,6 +47,11 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera { ); } + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * + * @param adjustment the adjustment to be made to the camera + */ private void adjustZoomFactor(Double adjustment) { if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { zoomFactor = zoomFactor + adjustment; @@ -51,6 +59,10 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ private void adjustVerticalPan(Double adjustment) { if (verticalPan + adjustment >= MIN_Y && verticalPan + adjustment <= MAX_Y) { verticalPan += adjustment; @@ -58,6 +70,10 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ private void adjustHorizontalPan(Double adjustment) { if (horizontalPan + adjustment >= MIN_X && horizontalPan + adjustment <= MIN_Y) { this.horizontalPan += adjustment; diff --git a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java index 09455345..72d58707 100644 --- a/src/main/java/seng302/visualiser/cameras/TopDownCamera.java +++ b/src/main/java/seng302/visualiser/cameras/TopDownCamera.java @@ -32,6 +32,11 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { verticalPan = 0.0; } + /** + * Sets a player boat object to observe and update the camera with. + * + * @param playerBoat The player boat to be observed. + */ public void setPlayerBoat(BoatObject playerBoat) { this.playerBoat = playerBoat; @@ -41,6 +46,9 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Moves the camera to a new position after some change (Zooming or Panning) + */ private void updateCamera() { transforms.clear(); transforms.addAll( @@ -49,6 +57,10 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { ); } + /** + * Adjusts the zoom amount (camera depth) by some adjustment value + * @param adjustment the adjustment to be made to the camera + */ private void adjustZoomFactor(Double adjustment) { if (zoomFactor + adjustment < NEAR_ZOOM_LIMIT && zoomFactor + adjustment > FAR_ZOOM_LIMIT) { zoomFactor = zoomFactor + adjustment; @@ -56,6 +68,10 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Vertical Panning of the Camera + * @param adjustment the adjustment to be made to the camera + */ private void adjustVerticalPan(Double adjustment) { if (verticalPan + adjustment >= -PAN_LIMIT && verticalPan + adjustment <= PAN_LIMIT) { verticalPan += adjustment; @@ -63,6 +79,10 @@ public class TopDownCamera extends PerspectiveCamera implements RaceCamera { } } + /** + * Adjusts the Horizontal Panning of the Camera. + * @param adjustment the adjustment to be made to the camera + */ private void adjustHorizontalPan(Double adjustment) { if (horizontalPan + adjustment >= -PAN_LIMIT && horizontalPan + adjustment <= PAN_LIMIT) { horizontalPan += adjustment; From b5076bc9762d83a01d625158ce2a7392a2511360 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 26 Sep 2017 18:57:15 +1300 Subject: [PATCH 31/42] 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 32/42] 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 33/42] 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 34/42] 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 36/42] 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 37/42] 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 38/42] 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 39/42] 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 40/42] 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 41/42] 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 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 U4yb(b?=Ga7k(FT`>~{SB0iX?9EdT%j literal 3580 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 literal 3205 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 literal 4328 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 WwD@H(bH7^v0000IV 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

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 42/42] 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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