diff --git a/localscores.txt b/localscores.txt new file mode 100644 index 0000000000000000000000000000000000000000..cdc8ab931b2561c192d765113f5f7e208752d26f --- /dev/null +++ b/localscores.txt @@ -0,0 +1,11 @@ +test10:10000 +test9:9000 +test8:8000 +test7:7000 +test6:6000 +bob:5510 +tg:5360 +test5:5000 +test4:4000 +test3:3000 +test2:2000 diff --git a/tetrecs/localscores.txt b/tetrecs/localscores.txt new file mode 100644 index 0000000000000000000000000000000000000000..580fb291bd0d28dd72d860177e825cc8f8f52f66 --- /dev/null +++ b/tetrecs/localscores.txt @@ -0,0 +1,11 @@ +w:21950 +2:21850 +test6:21850 +test1234:2700 +Ayaz:2690 +aa:2290 +Player2:1890 +Player1:540 +t:540 +a:450 +rtwe:350 diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/LoadingBoard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/LoadingBoard.java new file mode 100644 index 0000000000000000000000000000000000000000..e10808a7faf2cb984d0f87fbc220830271f20fe3 --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/LoadingBoard.java @@ -0,0 +1,23 @@ +package uk.ac.soton.comp1206.component; + +import uk.ac.soton.comp1206.game.GamePiece; + +public class LoadingBoard extends GameBoard{ + public LoadingBoard(double width, double height) { + super(5,5, width, height); + this.isPieceBoard = true; + } + + public void displayPiece(GamePiece piece, int placeX, int placeY) { + clear(); + this.grid.playPiece(piece, placeX + 1, placeY + 1); + } + + public void displayPiece(GamePiece piece) { + displayPiece(piece, 0, 0); + } + + public void clear() { + this.grid.clear(); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/ScoreUpdatedListener.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/ScoreUpdatedListener.java index c88cfdb38c9fd55ad276d24847e757aba98bafda..18576c5c61d2a1308f8f3a97ea76bba5eaaa21ed 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/ScoreUpdatedListener.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/ScoreUpdatedListener.java @@ -1,5 +1,6 @@ package uk.ac.soton.comp1206.event; public interface ScoreUpdatedListener { - void updateHighScore(); + void updateScore(); + } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/ChallengeGame.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/ChallengeGame.java new file mode 100644 index 0000000000000000000000000000000000000000..5f6be97cccaca1aca26f53d6356187a06d1ee545 --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/ChallengeGame.java @@ -0,0 +1,35 @@ +package uk.ac.soton.comp1206.game; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Random; + +public class ChallengeGame extends Game{ + private static final Logger logger = LogManager.getLogger(ChallengeGame.class); + /** + * Create a new game with the specified rows and columns. Creates a corresponding grid model. + * + * @param cols number of columns + * @param rows number of rows + */ + public ChallengeGame(int cols, int rows) { + super(cols, rows); + } + + @Override + public GamePiece spawnPiece() { + Random rand = new Random(); + GamePiece piece = GamePiece.createPiece(rand.nextInt(15), rand.nextInt(3)); + logger.info("Spawning piece: {}", piece); + return piece; + } + + @Override + public void sendChatMessage(String message) { + } + + @Override + public void updateLeaderboard(String[] data) { + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java index 68ab9a82ba4991acb6089294d09ca3612531496b..f3561547919ae8f5019ea7298b57c4b1c0f9e4e2 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Game.java @@ -3,6 +3,11 @@ package uk.ac.soton.comp1206.game; import javafx.application.Platform; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.util.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.component.GameBlock; @@ -16,7 +21,7 @@ import java.util.*; * The Game class handles the main logic, state and properties of the TetrECS game. Methods to manipulate the game state * and to handle actions made by the player should take place inside this class. */ -public class Game { +public abstract class Game { private static final Logger logger = LogManager.getLogger(Game.class); @@ -38,16 +43,19 @@ public class Game { protected GamePiece currentPiece; protected GamePiece nextPiece; - private IntegerProperty score = new SimpleIntegerProperty(0); - private IntegerProperty level = new SimpleIntegerProperty(0); - private IntegerProperty lives = new SimpleIntegerProperty(0); - private IntegerProperty multiplier = new SimpleIntegerProperty(0); + protected IntegerProperty score = new SimpleIntegerProperty(0); + protected IntegerProperty level = new SimpleIntegerProperty(0); + protected IntegerProperty lives = new SimpleIntegerProperty(0); + protected IntegerProperty multiplier = new SimpleIntegerProperty(0); + protected SimpleStringProperty myName = new SimpleStringProperty(""); protected NextPieceListener nextPieceListener = null; protected LineClearedListener lineClearedListener = null; protected GameOverListener gameOverListener = null; protected GameLoopListener gameLoopListener = null; protected ScoreUpdatedListener scoreUpdatedListener = null; + protected ObservableList<Pair<String, Integer>> leaderboardList = FXCollections.observableArrayList(); + protected SimpleStringProperty chatMessage = new SimpleStringProperty("Press T to type in chat"); protected Timer timer; @@ -114,12 +122,7 @@ public class Game { } } - public GamePiece spawnPiece() { - Random rand = new Random(); - GamePiece piece = GamePiece.createPiece(rand.nextInt(15), rand.nextInt(3)); - logger.info("Spawning piece: {}", piece); - return piece; - } + abstract GamePiece spawnPiece(); public void nextPiece() { this.currentPiece = this.nextPiece; @@ -309,6 +312,16 @@ public class Game { return nextPiece; } + public ObservableList<Pair<String, Integer>> getLeaderboardList() { + return leaderboardList; + } + + public SimpleStringProperty getChatMessage() { + return chatMessage; + } + + public abstract void sendChatMessage(String message); + public void setNextPieceListener(NextPieceListener listener) { this.nextPieceListener = listener; } @@ -349,6 +362,14 @@ public class Game { return multiplier; } + public StringProperty getMyName() { + return myName; + } + + public void setMyName(String name) { + this.myName.set(name); + } + public int getTimerDelay() { return Math.max(2500, 12000 - 500 * this.level.get()); } @@ -358,7 +379,7 @@ public class Game { this.score.set(score); if (this.scoreUpdatedListener != null) { - scoreUpdatedListener.updateHighScore(); + scoreUpdatedListener.updateScore(); } } @@ -383,4 +404,6 @@ public class Game { } this.multiplier.set(multiplier); } + + public abstract void updateLeaderboard(String[] data); } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/GamePiece.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/GamePiece.java index 627ef2de43159d5632c384b4dd519bbb585507b8..d7fa689ab321eba3dc0d6ce57282c2907865aca5 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/GamePiece.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/GamePiece.java @@ -132,6 +132,45 @@ public class GamePiece { throw new IndexOutOfBoundsException("No such piece: " + piece); } + public static GamePiece createLoadingPiece(String letter, int colour) { + switch (letter) { + case "T" -> { + int[][] blocks = {{1,0,0,0,0},{1,0,0,0,0},{1,1,1,1,1},{1,0,0,0,0},{1,0,0,0,0}}; + return new GamePiece("T", blocks, colour); + } + case "E" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,1,1},{1,0,1,0,1},{1,0,1,0,1},{0,0,0,0,0}}; + return new GamePiece("E", blocks,colour); + } + case "R" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,1,1},{1,0,1,0,0},{1,0,1,1,0},{1,1,0,1,1}}; + return new GamePiece("R", blocks,colour); + } + case "C" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,1,1},{1,0,0,0,1},{1,0,0,0,1},{0,0,0,0,0}}; + return new GamePiece("C", blocks,colour); + } + case "S" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,0,1},{1,0,1,0,1},{1,0,1,1,1},{0,0,0,0,0}}; + return new GamePiece("S", blocks,colour); + } + case "M" -> { + int[][] blocks = {{1,1,1,1,1},{1,0,0,0,0},{0,1,1,0,0},{1,0,0,0,0},{1,1,1,1,1}}; + return new GamePiece("M", blocks,colour); + } + case "A" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,1,1},{1,0,1,0,0},{1,1,1,1,1},{0,0,0,0,0}}; + return new GamePiece("A", blocks,colour); + } + case "G" -> { + int[][] blocks = {{0,0,0,0,0},{1,1,1,1,1},{1,0,0,0,1},{1,0,1,0,1},{1,0,1,1,1}}; + return new GamePiece("G", blocks,colour); + } + } + + throw new IndexOutOfBoundsException("No such piece: " + letter); + } + /** * Create a new GamePiece of the specified piece number and rotation * @@ -146,6 +185,12 @@ public class GamePiece { return newPiece; } + public static GamePiece createLoadingPiece(String piece,int colour, int rotation) { + var newPiece = createLoadingPiece(piece, colour); + newPiece.rotateLoadingBoards(rotation); + return newPiece; + } + /** * Create a new GamePiece with the given name, block makeup and value. Should not be called directly, only via the * factory. @@ -217,6 +262,28 @@ public class GamePiece { blocks = rotated; } + public void rotateLoadingBoards(int rotations) { + for (int rotated = 0; rotated < rotations; rotated++) { + rotateLoadingBoards(); + } + } + + public void rotateLoadingBoards() { + int[][] result = new int[blocks.length][]; + + for(int i = 0; i < blocks.length; i++) { + result[i] = new int[blocks[i].length]; + System.arraycopy(blocks[i], 0, result[i], 0, blocks[i].length); + } + + for(int x = 0; x < 5; x++) { + for(int y = 0; y < 5; y++) { + blocks[x][y] = result[5 - y - 1][x]; + } + } + + } + /** * Return the string representation of this piece diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/MultiplayerGame.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/MultiplayerGame.java new file mode 100644 index 0000000000000000000000000000000000000000..5458649e0449f630aa9c8387d84a672e15d04200 --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/MultiplayerGame.java @@ -0,0 +1,164 @@ +package uk.ac.soton.comp1206.game; + +import javafx.application.Platform; +import javafx.util.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import uk.ac.soton.comp1206.network.Communicator; +import uk.ac.soton.comp1206.utility.Multimedia; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Random; + +public class MultiplayerGame extends Game{ + private static final Logger logger = LogManager.getLogger(MultiplayerGame.class); + private Communicator communicator; + + private ArrayList<Pair<String, Integer>> onlineScores = new ArrayList<>(); + private ArrayDeque<GamePiece> pieces; + private boolean gameStarted = false; + + /** + * Create a new game with the specified rows and columns. Creates a corresponding grid model. + * + * @param cols number of columns + * @param rows number of rows + */ + public MultiplayerGame(Communicator communicator, int cols, int rows) { + super(cols, rows); + + this.communicator = communicator; + } + + @Override + public void initialiseGame() { + logger.info("Initialising Multiplayer Game"); + + this.score.set(0); + this.level.set(0); + this.lives.set(3); + this.multiplier.set(1); + this.pieces = new ArrayDeque<>(); + + this.communicator.addListener(data -> { + Platform.runLater(() -> receiveCommunication(data)); + }); + this.communicator.send("SCORES"); + + for (int i = 0; i < 3; i++) { + this.communicator.send("PIECE"); + } + } + + private void receiveCommunication(String message) { + String[] components = message.split(" ", 2); + switch (components[0]) { + case "MSG" -> { + logger.info("Received message"); + Multimedia.playSound("message.wav"); + String[] data = components[1].split(":", 2); + parseMessage(data); + } + case "SCORE" -> { + String[] data = components[1].split(":"); + updateLeaderboard(data); + } + case "SCORES" -> { + String[] scores = components[1].split("\n"); + loadScores(scores); + } + case "PIECE" -> { + int value = Integer.parseInt(components[1]); + Random random = new Random(); + this.pieces.add(GamePiece.createPiece(value, random.nextInt(3))); + if (this.pieces.size() > 2 && !this.gameStarted) { + this.nextPiece = spawnPiece(); + nextPiece(); + gameStarted = true; + } + } + } + } + + private void updateScore() { + this.communicator.send("SCORE " + this.getScore().get()); + } + + private void parseMessage(String[] data) { + if (data.length > 1) { + chatMessage.set("[" + data[0] + "]:" + data[1]); + } else { + chatMessage.set("[" + data[0] + "]:"); + } + } + + public void updateLeaderboard(String[] score) { + int i = -1; + for (Pair<String, Integer> pair : leaderboardList) { + if (pair.getKey().equals(score[0])) { + i = leaderboardList.indexOf(pair); + } + } + + if (i != -1) { + leaderboardList.set(i, new Pair<>(score[0], Integer.parseInt(score[1]))); + leaderboardList.sort(new Comparator<Pair<String, Integer>>() { + @Override + public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { + if (l1.getValue() > l2.getValue()) { + return -1; + } else if (l1.getValue().equals(l2.getValue())) { + return 0; + } else { + return 1; + } + } + }); + } + } + + private void loadScores(String[] scores) { + onlineScores.clear(); + for (String score : scores) { + String[] data = score.split(":"); + onlineScores.add(new Pair<>(data[0], Integer.parseInt(data[1]))); + } + onlineScores.sort(new Comparator<Pair<String, Integer>>() { + @Override + public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { + if (l1.getValue() > l2.getValue()) { + return -1; + } else if (l1.getValue().equals(l2.getValue())) { + return 0; + } else { + return 1; + } + } + }); + + leaderboardList.clear(); + leaderboardList.addAll(this.onlineScores); + } + + public void sendChatMessage(String message) { + this.communicator.send("MSG " + message); + } + + @Override + public GamePiece spawnPiece() { + logger.info("Spawning Piece"); + this.communicator.send("PIECE"); + return this.pieces.pop(); + } + + @Override + public void setScore(int score) { + logger.info("Score is now " + score); + this.score.set(score); + + this.updateScore(); + } + +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java index 7ebc981c6eb4f216cdc20daa67c2c76244a5f178..360ffc22b206e4dba4b21c5174b92f7274dc9c18 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ChallengeScene.java @@ -16,6 +16,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.component.*; +import uk.ac.soton.comp1206.game.ChallengeGame; import uk.ac.soton.comp1206.game.Game; import uk.ac.soton.comp1206.game.GamePiece; import uk.ac.soton.comp1206.ui.GamePane; @@ -30,7 +31,7 @@ import java.util.HashSet; */ public class ChallengeScene extends BaseScene { - private static final Logger logger = LogManager.getLogger(MenuScene.class); + private static final Logger logger = LogManager.getLogger(ChallengeScene.class); protected Game game; protected GameBoard board; @@ -38,8 +39,13 @@ public class ChallengeScene extends BaseScene { protected PieceBoard nextPieceBoard; protected GameLoopTimer timer; - private int keyBoardPosX = 0; - private int keyBoardPosY = 0; + protected int keyBoardPosX = 0; + protected int keyBoardPosY = 0; + + protected BorderPane mainPane; + protected Text gameTitle; + protected HBox header; + protected VBox gameInfo; private IntegerProperty hiscore = new SimpleIntegerProperty(0); @@ -78,7 +84,7 @@ public class ChallengeScene extends BaseScene { }); }); - game.start(); + this.game.start(); } private void updateHiScore() { @@ -125,7 +131,7 @@ public class ChallengeScene extends BaseScene { public void build() { logger.info("Building " + this.getClass().getName()); - setupGame(); + this.setupGame(); root = new GamePane(gameWindow.getWidth(), gameWindow.getHeight()); @@ -135,7 +141,7 @@ public class ChallengeScene extends BaseScene { challengePane.getStyleClass().add("menu-background"); root.getChildren().add(challengePane); - BorderPane mainPane = new BorderPane(); + mainPane = new BorderPane(); challengePane.getChildren().add(mainPane); //Header of Challenge Scene @@ -147,38 +153,35 @@ public class ChallengeScene extends BaseScene { timer = new GameLoopTimer(this.gameWindow.getWidth()); mainPane.setBottom(timer); - } public HBox buildHeader() { //Header of Challenge Scene - HBox header = new HBox(); + header = new HBox(); //VBox to hold the Score title and value VBox scoreBox = new VBox(); - var scoreTitle = new Text("Score"); + Text scoreTitle = new Text("Score"); scoreTitle.getStyleClass().add("heading"); - var scoreValue = new Text(); - scoreValue.textProperty().bind(game.getScore().asString()); + Text scoreValue = new Text(); + scoreValue.textProperty().bind(this.game.getScore().asString()); scoreValue.getStyleClass().add("score"); scoreBox.getChildren().addAll(scoreTitle, scoreValue); scoreBox.setAlignment(Pos.BASELINE_CENTER); header.getChildren().add(scoreBox); - //Challenge scene title - var challengeTitle = new Text("Challenge Mode"); - challengeTitle.getStyleClass().add("title"); - header.getChildren().add(challengeTitle); - + gameTitle = new Text("Challenge Mode"); + gameTitle.getStyleClass().add("title"); + header.getChildren().add(gameTitle); //VBox to store lives title and value VBox livesBox = new VBox(); - var livesTitle = new Text("Lives"); + Text livesTitle = new Text("Lives"); livesTitle.getStyleClass().add("heading"); - var livesValue = new Text(); - livesValue.textProperty().bind(game.getLives().asString()); + Text livesValue = new Text(); + livesValue.textProperty().bind(this.game.getLives().asString()); livesValue.getStyleClass().add("lives"); livesBox.getChildren().addAll(livesTitle, livesValue); livesBox.setAlignment(Pos.BASELINE_CENTER); @@ -194,34 +197,34 @@ public class ChallengeScene extends BaseScene { public HBox buildBoards() { HBox boards = new HBox(); - this.board = new GameBoard(game.getGrid(), gameWindow.getWidth() / 2, gameWindow.getWidth() / 2); + this.board = new GameBoard(this.game.getGrid(), gameWindow.getWidth() / 2, gameWindow.getWidth() / 2); //Handle block on gameboard grid being clicked this.board.setOnBlockClick(this::blockClicked); this.board.setOnRightClick(this::rotateBlock); boards.getChildren().add(this.board); - VBox gameInfo = new VBox(); + gameInfo = new VBox(); - var hiScoreTitle = new Text("High Score"); + Text hiScoreTitle = new Text("High Score"); hiScoreTitle.getStyleClass().add("heading"); gameInfo.getChildren().add(hiScoreTitle); - var hiScoreVal = new Text(); + Text hiScoreVal = new Text(); hiScoreVal.textProperty().bind(this.hiscore.asString()); hiScoreVal.getStyleClass().add("hiscore"); gameInfo.getChildren().add(hiScoreVal); - var levelTitle = new Text("Level"); + Text levelTitle = new Text("Level"); levelTitle.getStyleClass().add("heading"); gameInfo.getChildren().add(levelTitle); - var levelVal = new Text(); - levelVal.textProperty().bind(game.getLevel().asString()); + Text levelVal = new Text(); + levelVal.textProperty().bind(this.game.getLevel().asString()); levelVal.getStyleClass().add("level"); gameInfo.getChildren().add(levelVal); - var incomingTitle = new Text("Incoming"); + Text incomingTitle = new Text("Incoming"); incomingTitle.getStyleClass().add("heading"); gameInfo.getChildren().add(incomingTitle); @@ -246,17 +249,17 @@ public class ChallengeScene extends BaseScene { return boards; } - private void gameTimer(int delay) { + protected void gameTimer(int delay) { logger.info("Running game loop animation"); timer.setupFrames(delay); timer.startAnimation(); } - private void swapPiece(GameBlock gameBlock) { + protected void swapPiece(GameBlock gameBlock) { swapPiece(); } - private void swapPiece() { + protected void swapPiece() { logger.info("Swapping current and next piece"); Multimedia.playSound("rotate.wav"); this.currentPieceBoard.displayPiece(this.game.getNextPiece()); @@ -269,28 +272,28 @@ public class ChallengeScene extends BaseScene { * * @param gameBlock the Game Block that was clocked */ - private void blockClicked(GameBlock gameBlock) { + protected void blockClicked(GameBlock gameBlock) { this.keyBoardPosX = gameBlock.getX(); this.keyBoardPosY = gameBlock.getY(); game.blockClicked(gameBlock); } - private void rotateBlock(int rotations) { + protected void rotateBlock(int rotations) { logger.info("Rotating block"); Multimedia.playSound("rotate.wav"); this.game.rotatePiece(rotations); this.currentPieceBoard.displayPiece(this.game.getCurrentPiece()); } - private void rotateBlock(GameBlock gameBlock) { + protected void rotateBlock(GameBlock gameBlock) { rotateBlock(1); } - private void rotateBlock() { + protected void rotateBlock() { rotateBlock(1); } - private void lineCleared(HashSet<GameBlockCoordinate> blocksToRemove) { + protected void lineCleared(HashSet<GameBlockCoordinate> blocksToRemove) { if (blocksToRemove.size() == 0) return; Multimedia.playSound("clear.wav"); for (GameBlockCoordinate block : blocksToRemove) { @@ -310,7 +313,7 @@ public class ChallengeScene extends BaseScene { logger.info("Starting a new challenge"); //Start new game - game = new Game(5, 5); + game = new ChallengeGame(5, 5); } protected void nextPiece(GamePiece nextPiece) { diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/InstructionScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/InstructionScene.java index 34c1097fa21f1dbb0ead87bbbe63e692625e5561..98e620f41a97fd1940c41fb4faa86c6b01a93562 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/InstructionScene.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/InstructionScene.java @@ -75,7 +75,7 @@ public class InstructionScene extends BaseScene{ ImageView instructionsImage = new ImageView(getClass().getResource("/images/Instructions.png").toExternalForm()); instructionsImage.setPreserveRatio(true); - instructionsImage.setFitWidth(this.gameWindow.getWidth() / 1.5); + instructionsImage.setFitWidth(this.gameWindow.getWidth() / 1.6); widgets.getChildren().add(instructionsImage); Text gamePiecesLabel = new Text("Game Pieces"); @@ -119,6 +119,7 @@ public class InstructionScene extends BaseScene{ } private void displayNextPiece(MouseEvent event) { + Multimedia.playSound("rotate.wav"); if (pieceNumber != 14) { pieceNumber++; } else { @@ -130,6 +131,7 @@ public class InstructionScene extends BaseScene{ } private void displayPrevPiece(MouseEvent event) { + Multimedia.playSound("rotate.wav"); if (pieceNumber != 0) { pieceNumber--; } else { diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LoadingScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LoadingScene.java new file mode 100644 index 0000000000000000000000000000000000000000..0a82b16a433db0068b73244b5341f92bdee2c16c --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LoadingScene.java @@ -0,0 +1,141 @@ +package uk.ac.soton.comp1206.scene; + +import javafx.animation.ScaleTransition; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.geometry.Pos; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.util.Duration; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import uk.ac.soton.comp1206.component.LoadingBoard; +import uk.ac.soton.comp1206.game.GamePiece; +import uk.ac.soton.comp1206.ui.GamePane; +import uk.ac.soton.comp1206.ui.GameWindow; +import uk.ac.soton.comp1206.utility.Multimedia; + +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; + +public class LoadingScene extends BaseScene{ + private static final Logger logger = LogManager.getLogger(LoadingScene.class); + + private Timer timer; + private Timer pieceTimer; + private int pieceNum = 0; + private VBox titleBox; + private ArrayList<LoadingBoard> boards = new ArrayList<>(); + private int[] rotations = {2,2,3,3,2,3,3,4}; + private int[] colours = {8,8,8,10,10,10,10,10}; + + /** + * Create a new scene, passing in the GameWindow the scene will be displayed in + * + * @param gameWindow the game window + */ + public LoadingScene(GameWindow gameWindow) { + super(gameWindow); + } + + @Override + public void initialise() { + pieceTimer = new Timer(); + TimerTask pieceTask = new TimerTask() { + @Override + public void run() { + try { + placePiece(rotations[pieceNum]); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + + pieceTimer.scheduleAtFixedRate(pieceTask, 0, 850); + } + + @Override + public void build() { + logger.info("Building " + this.getClass().getName()); + + root = new GamePane(gameWindow.getWidth(), gameWindow.getHeight()); + + StackPane loadingPane = new StackPane(); + loadingPane.setMaxWidth(gameWindow.getWidth()); + loadingPane.setMaxHeight(gameWindow.getHeight()); + loadingPane.getStyleClass().add("intro"); + root.getChildren().add(loadingPane); + + BorderPane mainPane = new BorderPane(); + loadingPane.getChildren().add(mainPane); + + titleBox = new VBox(); + titleBox.setAlignment(Pos.CENTER); + titleBox.setSpacing(20); + mainPane.setCenter(titleBox); + + HBox ecs = new HBox(); + ecs.setAlignment(Pos.CENTER); + ecs.setSpacing(5); + + for (int i = 0; i < 3; i++) { + LoadingBoard grid = new LoadingBoard(gameWindow.getWidth() / 12, gameWindow.getWidth() / 12); + ecs .getChildren().add(grid); + boards.add(grid); + } + + HBox games = new HBox(); + games.setAlignment(Pos.CENTER); + games.setSpacing(5); + + for (int i = 0; i < 5; i++) { + LoadingBoard grid = new LoadingBoard(gameWindow.getWidth() / 12, gameWindow.getWidth() / 12); + games.getChildren().add(grid); + boards.add(grid); + } + + titleBox.getChildren().add(ecs); + titleBox.getChildren().add(games); + } + + public void placePiece(int rotations) throws InterruptedException { + String[] letters= {"E","C","S","G","A","M","E","S"}; + GamePiece piece = GamePiece.createLoadingPiece(letters[pieceNum], colours[pieceNum], rotations); + boards.get(pieceNum).displayPiece(piece); + + for (int i = 0; i < 4 - rotations; i++) { + Thread.sleep(200); + Multimedia.playSound("rotate.wav"); + piece.rotateLoadingBoards(1); + boards.get(pieceNum).displayPiece(piece); + } + + Multimedia.playSound("place.wav"); + + pieceNum++; + if (pieceNum == 8) { + pieceTimer.cancel(); + pieceTimer.purge(); + startSound(); + } + } + + public void startSound() { + Multimedia.playSound("intro.mp3"); + ScaleTransition st = new ScaleTransition(Duration.millis(6000),titleBox); + st.setToX(1.5); + st.setToY(1.5); + + st.play(); + st.setOnFinished(e -> {this.toMenu();}); + } + + + public void toMenu() { + Platform.runLater(this.gameWindow::startMenu); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LobbyScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LobbyScene.java new file mode 100644 index 0000000000000000000000000000000000000000..b9859579cbe1bf714ae641ff0de5a7175ae19cb9 --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/LobbyScene.java @@ -0,0 +1,423 @@ +package uk.ac.soton.comp1206.scene; + +import javafx.animation.FadeTransition; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; +import javafx.util.Duration; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import uk.ac.soton.comp1206.network.Communicator; +import uk.ac.soton.comp1206.ui.GamePane; +import uk.ac.soton.comp1206.ui.GameWindow; +import uk.ac.soton.comp1206.utility.Multimedia; + +import java.util.Timer; +import java.util.TimerTask; + +public class LobbyScene extends BaseScene{ + private static final Logger logger = LogManager.getLogger(LobbyScene.class); + + private Communicator communicator; + private Timer timer; + + private VBox currentGames; + private VBox createGame; + private VBox messages; + private HBox buttons; + private HBox playerBox; + private Label infoBox; + + private boolean isInChannel = false; + private SimpleStringProperty myName = new SimpleStringProperty(""); + + /** + * Create a new scene, passing in the GameWindow the scene will be displayed in + * + * @param gameWindow the game window + */ + public LobbyScene(GameWindow gameWindow) { + super(gameWindow); + + this.communicator = gameWindow.getCommunicator(); + logger.info("Creating lobby scene"); + } + + @Override + public void initialise() { + this.scene.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ESCAPE)) backToMenu(); + }); + + this.communicator.addListener(data -> { + Platform.runLater(() -> receiveCommunication(data)); + }); + + timer = new Timer(); + TimerTask task = new TimerTask() { + @Override + public void run() { + requestCurrentChannels(); + } + }; + timer.scheduleAtFixedRate(task, 0, 2000); + } + + public void receiveCommunication(String message) { + String[] components = message.split(" ", 2); + switch (components[0]) { + case "CHANNELS": + if (components.length > 1) { + String[] channels = components[1].split("\n"); + if (!channels[0].equals("")) { + this.showCurrentGames(channels); + } else { + noGamesFound(); + } + } else { + noGamesFound(); + } + break; + case "JOIN": + setupChat(); + isInChannel = true; + break; + case "PARTED": + startCreateOptions(); + break; + case "MSG": { + String[] data = components[1].split(":", 2); + parseMessage(data); + break; + } + case "HOST": { + Button start = new Button("Start"); + buttons.getChildren().add(0, start); + start.setOnAction(e -> { + startGame(); + }); + break; + } + case "USERS": { + playerBox.getChildren().clear(); + Multimedia.playSound("message.wav"); + String[] names = components[1].split("\n"); + for (String name : names) { + Text playerName = new Text(name); + if (name.equals(myName.get())) { + playerName.getStyleClass().add("myname"); + } + playerBox.getChildren().add(playerName); + } + break; + } + case "NICK": { + String[] data = components[1].split(":"); + if (data.length == 1) { + myName.set(data[0]); + infoBox.setText("Name Set to: " + myName.get()); + infoBox.getStyleClass().add("instructions"); + infoBox.setTextFill(Color.GREEN); + + FadeTransition ft = new FadeTransition(Duration.millis(5000), infoBox); + ft.setFromValue(1); + ft.setToValue(0); + ft.play(); + } + break; + } + case "START": { + Multimedia.stopSound(); + if (timer != null) { + timer.cancel(); + timer.purge(); + } + this.gameWindow.startMultiplayerGame(this.myName.get()); + } + break; + case "ERROR": { + if (components[1].equals("Channel already exists")) { + infoBox.setText("Error: There is already a game with that channel name!"); + infoBox.getStyleClass().add("instructions"); + infoBox.setTextFill(Color.RED); + } else if (components[1].equals("That name is already in use")) { + infoBox.setText("Error: That name is already in use!"); + infoBox.getStyleClass().add("instructions"); + infoBox.setTextFill(Color.RED); + } else if (components[1].equals("You are already in a channel")) { + infoBox.setText("Error: You are already in a channel!"); + infoBox.getStyleClass().add("instructions"); + infoBox.setTextFill(Color.RED); + } + + FadeTransition ft = new FadeTransition(Duration.millis(5000), infoBox); + ft.setFromValue(1); + ft.setToValue(0); + ft.play(); + + break; + } + } + } + + private void startGame() { + this.communicator.send("START"); + } + + private void requestCurrentChannels() { + this.communicator.send("LIST"); + } + + private void sendCreateRequest(String channelName) { + this.communicator.send("CREATE " + channelName); + } + + private void leaveChannel() { + logger.info("Leaving channel"); + isInChannel = false; + this.communicator.send("PART"); + } + + private void joinChannel(String channelName) { + if (!isInChannel) { + logger.info("Joining channel: {}", channelName); + this.communicator.send("JOIN " + channelName); + } + } + + private void sendMessage(String message) { + this.communicator.send("MSG " + message); + } + + private void parseMessage(String[] data) { + Multimedia.playSound("message.wav"); + Text message = new Text(); + if (data.length > 1) { + message.setText("[" + data[0] + "]:" + data[1]); + } else { + message.setText("[" + data[0] + "]:"); + } + + messages.getChildren().add(message); + } + + private void changeNickName(String name) { + logger.info("Changing nickname to {}", name); + this.communicator.send("NICK " + name); + } + + private void startCreateOptions() { + if (!isInChannel) { + createGame.getChildren().clear(); + Text channelNameTitle = new Text("Channel Name"); + channelNameTitle.getStyleClass().add("channelItem"); + createGame.getChildren().add(channelNameTitle); + + TextField channelName = new TextField(); + channelName.setPromptText("Enter Channel Name"); + channelName.requestFocus(); + createGame.getChildren().add(channelName); + + Button submit = new Button("Submit"); + createGame.getChildren().add(submit); + + infoBox = new Label(); + infoBox.setTextAlignment(TextAlignment.CENTER); + infoBox.setMaxWidth(createGame.getWidth()); + infoBox.setWrapText(true); + createGame.getChildren().add(infoBox); + + channelName.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ENTER)) { + sendCreateRequest(channelName.getText()); + } + }); + + submit.setOnAction(e -> { + sendCreateRequest(channelName.getText()); + }); + } + } + + private void setupChat() { + createGame.getChildren().clear(); + + playerBox = new HBox(); + playerBox.setSpacing(5); + playerBox.getStyleClass().add("playerBox"); + createGame.getChildren().add(playerBox); + + VBox instructions = new VBox(); + instructions.setAlignment(Pos.CENTER); + createGame.getChildren().add(instructions); + + Text welcome = new Text("Welcome to the Lobby!"); + Text nickInstructions = new Text("Use /nick [NewName] to change username"); + welcome.getStyleClass().add("instructions"); + nickInstructions.getStyleClass().add("instructions"); + instructions.getChildren().addAll(welcome,nickInstructions); + + ScrollPane chatBox = new ScrollPane(); + chatBox.getStyleClass().add("scroller"); + chatBox.setMinHeight(300); + + messages = new VBox(); + messages.setSpacing(5); + messages.getStyleClass().add("messages"); + chatBox.setContent(messages); + createGame.getChildren().add(chatBox); + + infoBox = new Label(); + infoBox.setTextAlignment(TextAlignment.CENTER); + infoBox.setMaxWidth(createGame.getWidth()); + infoBox.setWrapText(true); + createGame.getChildren().add(infoBox); + + TextField messageField = new TextField(); + messageField.setPromptText("Enter message"); + messageField.requestFocus(); + messageField.getStyleClass().add("TextField"); + messageField.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ENTER)) { + if (messageField.getText().startsWith("/nick")) { + String[] data = messageField.getText().split(" "); + changeNickName(data[1]); + } else { + sendMessage(messageField.getText()); + } + messageField.clear(); + } + }); + + createGame.getChildren().add(messageField); + + buttons = new HBox(); + + Button leave = new Button("Leave"); + + leave.setOnAction(e -> { + this.leaveChannel(); + }); + + buttons.getChildren().add(leave); + buttons.setAlignment(Pos.CENTER); + buttons.setSpacing(220); + createGame.getChildren().add(buttons); + } + + @Override + public void build() { + logger.info("Building " + this.getClass().getName()); + + root = new GamePane(gameWindow.getWidth(), gameWindow.getHeight()); + + StackPane lobbyPane = new StackPane(); + lobbyPane.setMaxWidth(gameWindow.getWidth()); + lobbyPane.setMaxHeight(gameWindow.getHeight()); + lobbyPane.getStyleClass().add("menu-background"); + root.getChildren().add(lobbyPane); + + BorderPane mainPane = new BorderPane(); + lobbyPane.getChildren().add(mainPane); + + VBox widgets = new VBox(); + + Text lobbyTitle = new Text("Multiplayer Lobby"); + lobbyTitle.getStyleClass().add("title"); + widgets.getChildren().add(lobbyTitle); + + HBox multiplayerGames = new HBox(); + multiplayerGames.setSpacing(50); + multiplayerGames.setAlignment(Pos.CENTER); + widgets.getChildren().add(multiplayerGames); + + VBox currentGameVBox = new VBox(); + currentGameVBox.setAlignment(Pos.TOP_CENTER); + currentGameVBox.setSpacing(10); + multiplayerGames.getChildren().add(currentGameVBox); + + VBox createGameVBox = new VBox(); + createGameVBox.setAlignment(Pos.TOP_CENTER); + createGameVBox.setSpacing(10); + multiplayerGames.getChildren().add(createGameVBox); + + Text currentGamesTitle = new Text("Current Games"); + currentGamesTitle.getStyleClass().add("heading"); + currentGameVBox.getChildren().add(currentGamesTitle); + + Text hostGameTitle = new Text("Host New Game"); + hostGameTitle.getStyleClass().add("lobbyItem"); + hostGameTitle.setOnMouseClicked(e -> this.startCreateOptions()); + createGameVBox.getChildren().add(hostGameTitle); + + currentGames = new VBox(); + currentGames.setAlignment(Pos.CENTER); + currentGames.setSpacing(10); + currentGames.getStyleClass().add("gameBox"); + currentGames.setMinWidth(300); + currentGames.setMinHeight(500); + currentGameVBox.getChildren().add(currentGames); + + createGame = new VBox(); + createGame.getStyleClass().add("gameBox"); + createGame.setMinWidth(350); + createGame.setMinHeight(500); + createGame.setAlignment(Pos.TOP_CENTER); + createGame.setSpacing(10); + createGameVBox.getChildren().add(createGame); + + widgets.setAlignment(Pos.TOP_CENTER); + widgets.setSpacing(10); + + mainPane.setCenter(widgets); + } + + public void showCurrentGames(String[] channels) { + logger.info("Current number of available games: {}", channels.length); + + currentGames.getChildren().clear(); + + for (String channel : channels) { + Text channelName = new Text(channel); + channelName.getStyleClass().add("channelItem"); + channelName.setOnMouseClicked(e -> { + this.joinChannel(channelName.getText()); + }); + currentGames.getChildren().add(channelName); + } + } + + public void noGamesFound() { + logger.info("No current multiplayer games available"); + currentGames.getChildren().clear(); + + ProgressIndicator pi = new ProgressIndicator(); + currentGames.getChildren().add(pi); + + Text looking = new Text("Searching for games..."); + looking.getStyleClass().add("heading"); + currentGames.getChildren().add(looking); + } + + private void backToMenu() { + Multimedia.stopSound(); + if (timer != null) { + timer.cancel(); + timer.purge(); + } + if (isInChannel) { + this.communicator.send("PART"); + } + this.gameWindow.startMenu(); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java index cf60f59754cced920e054e8289b630f1ddee83b9..a0ea97303eb39c952015487a11ff671d04e27ad8 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java @@ -4,17 +4,22 @@ import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.media.MediaPlayer; import javafx.scene.text.Text; +import javafx.util.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.App; +import uk.ac.soton.comp1206.component.LoadingBoard; +import uk.ac.soton.comp1206.game.GamePiece; import uk.ac.soton.comp1206.ui.GamePane; import uk.ac.soton.comp1206.ui.GameWindow; import uk.ac.soton.comp1206.utility.Multimedia; + /** * The main menu of the game. Provides a gateway to the rest of the game. */ @@ -47,15 +52,13 @@ public class MenuScene extends BaseScene { menuPane.getStyleClass().add("menu-background"); root.getChildren().add(menuPane); - var mainPane = new BorderPane(); + BorderPane mainPane = new BorderPane(); menuPane.getChildren().add(mainPane); VBox menuWidgets = new VBox(); //Awful title - ImageView title = new ImageView(getClass().getResource("/images/TetrECS.png").toExternalForm()); - title.setPreserveRatio(true); - title.setFitWidth(this.gameWindow.getWidth() / 1.25); + HBox title = getGridTitle(); menuWidgets.getChildren().add(title); VBox menuButtons = new VBox(); @@ -67,6 +70,7 @@ public class MenuScene extends BaseScene { Text multiPlayLabel = new Text("Multi Player"); multiPlayLabel.getStyleClass().add("menuItem"); + multiPlayLabel.setOnMouseClicked(this::startLobby); menuButtons.getChildren().add(multiPlayLabel); Text instructions = new Text("How To Play"); @@ -74,6 +78,11 @@ public class MenuScene extends BaseScene { instructions.setOnMouseClicked(this::showInstructions); menuButtons.getChildren().add(instructions); + Text options = new Text("Options"); + options.getStyleClass().add("menuItem"); + options.setOnMouseClicked(this::showOptions); + menuButtons.getChildren().add(options); + Text exitLabel = new Text("Exit"); exitLabel.getStyleClass().add("menuItem"); exitLabel.setOnMouseClicked(e -> shutDown()); @@ -95,8 +104,10 @@ public class MenuScene extends BaseScene { */ @Override public void initialise() { - Multimedia.playBackgroundMusic("menumusic.mp3", true); - this.scene.setOnKeyPressed(e -> shutDown()); + Multimedia.playBackgroundMusic("menuMusic.wav", true); + this.scene.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ESCAPE)) shutDown(); + }); } /** @@ -108,10 +119,33 @@ public class MenuScene extends BaseScene { gameWindow.startChallenge(); } + private HBox getGridTitle() { + HBox grids = new HBox(); + grids.setSpacing(5); + grids.setAlignment(Pos.CENTER); + String[] letters = {"T","E","T","R","E","C","S"}; + int[] colours = {2,3,4,5,10,13,15}; + for (int i = 0; i < 7; i++) { + LoadingBoard lb = new LoadingBoard(gameWindow.getWidth() / 8, gameWindow.getWidth() / 8); + lb.displayPiece(GamePiece.createLoadingPiece(letters[i], colours[i])); + grids.getChildren().add(lb); + } + + return grids; + } + + private void startLobby(MouseEvent event) { + gameWindow.startLobby(); + } + private void showInstructions(MouseEvent event) { gameWindow.displayInstructions(); } + private void showOptions(MouseEvent event) { + gameWindow.showOptions(); + } + private void shutDown() { App.getInstance().shutdown(); } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MultiplayerScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MultiplayerScene.java new file mode 100644 index 0000000000000000000000000000000000000000..95c2a92d02db3b6a54c897d4eab2219d3d338b0a --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MultiplayerScene.java @@ -0,0 +1,182 @@ +package uk.ac.soton.comp1206.scene; + +import javafx.application.Platform; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.geometry.Pos; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import uk.ac.soton.comp1206.component.GameBoard; +import uk.ac.soton.comp1206.component.PieceBoard; +import uk.ac.soton.comp1206.game.MultiplayerGame; +import uk.ac.soton.comp1206.network.Communicator; +import uk.ac.soton.comp1206.ui.GameWindow; +import uk.ac.soton.comp1206.ui.Leaderboard; +import uk.ac.soton.comp1206.utility.Multimedia; + + +public class MultiplayerScene extends ChallengeScene{ + private static final Logger logger = LogManager.getLogger(MultiplayerScene.class); + + private boolean chatMode = false; + + private TextField chatInput; + private final Communicator communicator; + private SimpleStringProperty myName = new SimpleStringProperty(""); + private Leaderboard leaderboard; + + /** + * Create a new Single Player challenge scene + * + * @param gameWindow the Game Window + */ + public MultiplayerScene(GameWindow gameWindow, String name) { + super(gameWindow); + logger.info("Starting Multiplayer Scene"); + + this.myName.set(name); + this.communicator = gameWindow.getCommunicator(); + } + + @Override + public void initialise() { + logger.info("Initialising Multiplayer Scene"); + Multimedia.playBackgroundMusic("game_start.wav", false); + Multimedia.getBackgroundPlayer().setOnEndOfMedia(() -> Multimedia.playBackgroundMusic("game.wav", true)); + + this.game.setNextPieceListener(this::nextPiece); + this.game.setLineClearedListener(this::lineCleared); + this.game.setGameLoopListener(this::gameTimer); + this.game.setMyName(this.myName.get()); + + this.scene.setOnKeyPressed(this::keyHandler); + + this.game.setGameOverListener(() -> { + endGame(); + Platform.runLater(() -> { + this.gameWindow.showScores(this.game); + }); + }); + + this.communicator.addListener(data -> { + Platform.runLater(() -> receiveCommunication(data)); + }); + + this.game.start(); + } + + @Override + public void setupGame() { + logger.info("Starting a new multiplayer game"); + + game = new MultiplayerGame(this.communicator,5, 5); + } + + private void receiveCommunication(String message) { + String[] components = message.split(" "); + switch (components[0]) { + case "DIE" -> { + String player = components[1]; + leaderboard.setDead(player); + } + } + } + + private void sendMessage(String message) { + this.game.sendChatMessage(message); + } + + @Override + public void build() { + this.setupGame(); + super.build(); + + gameTitle.setText("Multiplayer Mode"); + + gameInfo.getChildren().subList(0, 4).clear(); + + leaderboard = new Leaderboard("Leaderboard"); + leaderboard.setName(this.myName.get()); + leaderboard.getScores().bind(new SimpleListProperty<>(this.game.getLeaderboardList())); + gameInfo.getChildren().add(0, leaderboard); + + VBox chatAndTimer = new VBox(); + + VBox chatMessages = new VBox(); + chatMessages.getStyleClass().add("messages"); + + Text chatMessage = new Text(" "); + chatMessage.textProperty().bind(this.game.getChatMessage()); + chatMessages.getChildren().add(chatMessage); + chatAndTimer.getChildren().add(chatMessages); + + chatInput = new TextField(); + chatInput.getStyleClass().add("TextField"); + chatInput.setVisible(false); + chatAndTimer.getChildren().add(chatInput); + + chatAndTimer.getChildren().add(timer); + + chatAndTimer.setSpacing(5); + + mainPane.setBottom(chatAndTimer); + } + + + private void keyHandler(KeyEvent e) { + this.board.setHover(this.board.getBlock(keyBoardPosX, keyBoardPosY), false); + if (e.getCode().equals(KeyCode.T)) { + chatMode = true; + chatInput.setVisible(true); + } else if (chatMode && e.getCode().equals(KeyCode.ESCAPE)) { + chatMode = false; + chatInput.setVisible(false); + } else if (chatMode && e.getCode().equals(KeyCode.ENTER)) { + sendMessage(chatInput.getText()); + chatInput.clear(); + chatMode = false; + chatInput.setVisible(false); + } else if (!chatMode) { + if (e.getCode().equals(KeyCode.ESCAPE)) { + endGame(); + this.gameWindow.startMenu(); + } else if (e.getCode().equals(KeyCode.ENTER) || e.getCode().equals(KeyCode.X)) { + this.blockClicked(this.board.getBlock(keyBoardPosX, keyBoardPosY)); + } else if (e.getCode().equals(KeyCode.SPACE) || e.getCode().equals(KeyCode.R)) { + this.swapPiece(); + } else if (e.getCode().equals(KeyCode.E) || e.getCode().equals(KeyCode.C) || e.getCode().equals(KeyCode.CLOSE_BRACKET)) { + this.rotateBlock(1); + } else if (e.getCode().equals(KeyCode.Q) || e.getCode().equals(KeyCode.Z) || e.getCode().equals(KeyCode.OPEN_BRACKET)) { + this.rotateBlock(3); + } else if (e.getCode().equals(KeyCode.UP) || e.getCode().equals(KeyCode.W)) { + if (keyBoardPosY > 0) keyBoardPosY--; + } else if (e.getCode().equals(KeyCode.DOWN) || e.getCode().equals(KeyCode.S)) { + if (keyBoardPosY < this.game.getRows() - 1) keyBoardPosY++; + } else if (e.getCode().equals(KeyCode.LEFT) || e.getCode().equals(KeyCode.A)) { + if (keyBoardPosX > 0) keyBoardPosX--; + } else if (e.getCode().equals(KeyCode.RIGHT) || e.getCode().equals(KeyCode.D)) { + if (keyBoardPosX < this.game.getCols() - 1) keyBoardPosX++; + } else if (e.getCode().equals(KeyCode.SHIFT)) { + endGame(); + Platform.runLater(() -> { + this.gameWindow.showScores(this.game); + }); + } + this.board.setHover(this.board.getBlock(keyBoardPosX, keyBoardPosY), true); + } + } + + @Override + public void endGame() { + super.endGame(); + this.communicator.send("DIE"); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/OptionsScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/OptionsScene.java new file mode 100644 index 0000000000000000000000000000000000000000..00326c2c55b52e30cff3ae5421c35ec5db9d23ea --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/OptionsScene.java @@ -0,0 +1,121 @@ +package uk.ac.soton.comp1206.scene; + +import javafx.beans.property.DoubleProperty; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import uk.ac.soton.comp1206.ui.GamePane; +import uk.ac.soton.comp1206.ui.GameWindow; +import uk.ac.soton.comp1206.utility.Multimedia; + +public class OptionsScene extends BaseScene{ + private static final Logger logger = LogManager.getLogger(OptionsScene.class); + + private static Slider musicVolumeSlider = new Slider(0,1,0.5); + + private static Slider soundVolumeSlider = new Slider(0,1,0.5); + /** + * Create a new scene, passing in the GameWindow the scene will be displayed in + * + * @param gameWindow the game window + */ + public OptionsScene(GameWindow gameWindow) { + super(gameWindow); + } + + @Override + public void initialise() { + this.scene.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ESCAPE)) { + this.backToMenu(); + } + }); + } + + @Override + public void build() { + logger.info("Building " + this.getClass().getName()); + + root = new GamePane(gameWindow.getWidth(), gameWindow.getHeight()); + + StackPane optionsPane = new StackPane(); + optionsPane.setMaxWidth(gameWindow.getWidth()); + optionsPane.setMaxHeight(gameWindow.getHeight()); + optionsPane.getStyleClass().add("menu-background"); + root.getChildren().add(optionsPane); + + BorderPane mainPane = new BorderPane(); + optionsPane.getChildren().add(mainPane); + + VBox widgets = new VBox(); + widgets.setAlignment(Pos.CENTER); + widgets.setSpacing(50); + mainPane.setTop(widgets); + + Text optionsTitle = new Text("Game Options"); + optionsTitle .getStyleClass().add("title"); + widgets.getChildren().add(optionsTitle); + + HBox options = new HBox(); + options.setAlignment(Pos.CENTER); + options.setSpacing(50); + widgets.getChildren().add(options); + + VBox musicOptions = new VBox(); + musicOptions.setAlignment(Pos.TOP_CENTER); + musicOptions.setSpacing(20); + musicOptions.getStyleClass().add("gameBox"); + musicOptions.setMinWidth(325); + musicOptions.setMaxHeight(400); + options.getChildren().add(musicOptions); + + Text musicTitle = new Text("Music Options"); + musicTitle.getStyleClass().add("heading"); + musicOptions.getChildren().add(musicTitle); + + VBox soundOptions = new VBox(); + soundOptions.setAlignment(Pos.TOP_CENTER); + soundOptions.setSpacing(20); + soundOptions.getStyleClass().add("gameBox"); + soundOptions.setMinWidth(325); + soundOptions.setMaxHeight(400); + options.getChildren().add(soundOptions); + + Text soundTitle = new Text("Sound Effects Options"); + soundTitle.getStyleClass().add("heading"); + soundOptions.getChildren().add(soundTitle); + + Text musicLabel = new Text("Music Volume"); + musicLabel.getStyleClass().add("channelItem"); + musicOptions.getChildren().add(musicLabel); + + musicOptions.getChildren().add(musicVolumeSlider); + + Text soundLabel = new Text("Sound Volume"); + soundLabel.getStyleClass().add("channelItem"); + soundOptions.getChildren().add(soundLabel); + + soundOptions.getChildren().add(soundVolumeSlider); + } + + public static DoubleProperty getMusicLevel() { + return musicVolumeSlider.valueProperty(); + } + + public static DoubleProperty getSoundLevel() { + return soundVolumeSlider.valueProperty(); + } + + private void backToMenu() { + Multimedia.stopSound(); + this.gameWindow.startMenu(); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ScoresScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ScoresScene.java index f3c2c357ccb516c98df76d6b1bf784ad19082841..333c8ebfe55ac69a26d63ce0ad6fe71cfe32c867 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ScoresScene.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/ScoresScene.java @@ -1,10 +1,7 @@ package uk.ac.soton.comp1206.scene; import javafx.application.Platform; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleListProperty; -import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Pos; @@ -17,19 +14,19 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; +import javafx.util.Duration; import javafx.util.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.game.Game; +import uk.ac.soton.comp1206.network.Communicator; import uk.ac.soton.comp1206.ui.GamePane; import uk.ac.soton.comp1206.ui.GameWindow; import uk.ac.soton.comp1206.ui.ScoresList; import uk.ac.soton.comp1206.utility.Multimedia; import java.io.*; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; +import java.util.*; public class ScoresScene extends BaseScene{ private static final Logger logger = LogManager.getLogger(ScoresScene.class); @@ -37,10 +34,21 @@ public class ScoresScene extends BaseScene{ private final Game game; private ObservableList<Pair<String, Integer>> localScores; + private ArrayList<Pair<String, Integer>> onlineScores = new ArrayList<>(); + private ObservableList<Pair<String, Integer>> remoteScores; private Pair<String, Integer> gameScore; private VBox widgets; private ScoresList localList; + private ScoresList onlineList; + + private final Communicator communicator; + + private boolean isLocalHiScore; + private boolean isOnlineHiScore; + private boolean checkingForScores = true; + private boolean isMultiGame = false; + private Timer timer; /** * Create a new scene, passing in the GameWindow the scene will be displayed in @@ -50,6 +58,7 @@ public class ScoresScene extends BaseScene{ public ScoresScene(GameWindow gameWindow, Game game) { super(gameWindow); this.game = game; + this.communicator = gameWindow.getCommunicator(); logger.info("Creating Scores Scene"); } @@ -60,7 +69,11 @@ public class ScoresScene extends BaseScene{ }); Multimedia.playSound("explode.wav"); Multimedia.playBackgroundMusic("end.wav", false); - Multimedia.getBackgroundPlayer().setOnEndOfMedia(this.gameWindow::startMenu); + + this.communicator.addListener(data -> { + Platform.runLater(() -> receiveCommunication(data)); + }); + loadOnlineScores(); } @Override @@ -69,7 +82,17 @@ public class ScoresScene extends BaseScene{ root = new GamePane(gameWindow.getWidth(), gameWindow.getHeight()); - this.localScores = FXCollections.observableArrayList(this.loadScores()); + if (this.game.getLeaderboardList().isEmpty()) { + this.localScores = FXCollections.observableArrayList(loadScores()); + localList = new ScoresList("Local Scores"); + } else { + isMultiGame = true; + this.localScores = this.game.getLeaderboardList(); + localList = new ScoresList("This Game"); + localList.setName(this.game.getMyName().get()); + } + this.remoteScores = FXCollections.observableArrayList(this.onlineScores); + onlineList = new ScoresList("Online Scores"); StackPane instructionsPane = new StackPane(); instructionsPane.setMaxWidth(gameWindow.getWidth()); @@ -91,18 +114,14 @@ public class ScoresScene extends BaseScene{ gameOver.getStyleClass().add("bigtitle"); widgets.getChildren().add(gameOver); - localList = new ScoresList("Local Scores"); - - checkHiScore(); + localList.getScores().bind(new SimpleListProperty<>(this.localScores)); + onlineList.getScores().bind(new SimpleListProperty<>(this.remoteScores)); HBox scoreLists = new HBox(); widgets.getChildren().add(scoreLists); scoreLists.getChildren().add(localList); - - Text onlineScoresTitle = new Text("Online Scores"); - onlineScoresTitle.getStyleClass().add("heading"); - scoreLists.getChildren().add(onlineScoresTitle); + scoreLists.getChildren().add(onlineList); scoreLists.setAlignment(Pos.CENTER); scoreLists.setSpacing(100); @@ -111,67 +130,194 @@ public class ScoresScene extends BaseScene{ widgets.setSpacing(20); mainPane.setCenter(widgets); - } public void checkHiScore() { - boolean isHiScore = false; + logger.info("Checking for High score"); + isLocalHiScore = false; + isOnlineHiScore = false; int score = this.game.getScore().get(); - for (Pair<String, Integer> nameScore : this.localScores) { - if (nameScore.getValue() < score) { - isHiScore = true; - break; + if (!isMultiGame) { + if (this.localScores.size() < 10 && score != 0) { + isLocalHiScore = true; + } else { + for (Pair<String, Integer> nameScore : this.localScores) { + if (nameScore.getValue() < score) { + isLocalHiScore = true; + break; + } + } } } - if (isHiScore) { + if (this.remoteScores.size() < 10 && score != 0) { + isOnlineHiScore = true; + } else { + for (Pair<String, Integer> nameScore : this.remoteScores) { + if (nameScore.getValue() < score) { + isOnlineHiScore = true; + break; + } + } + + } + + if (isLocalHiScore || isOnlineHiScore) { logger.info("User has got a high score"); Text hiScoreTitle = new Text("You've got a new High Score!"); hiScoreTitle.getStyleClass().add("title"); - widgets.getChildren().add(hiScoreTitle); - - TextField usernameEntry = new TextField(); - usernameEntry.setPromptText("Enter username"); - usernameEntry.requestFocus(); - widgets.getChildren().add(usernameEntry); - - Button submitBtn = new Button("Submit"); - widgets.getChildren().add(submitBtn); - - submitBtn.setOnAction(e -> { - this.localScores.add(new Pair<>(usernameEntry.getText(), score)); - - this.widgets.getChildren().remove(usernameEntry); - this.widgets.getChildren().remove(submitBtn); - - this.writeScores((List<Pair<String, Integer>>) localScores); - - this.localScores.sort(new Comparator<Pair<String, Integer>>() { - @Override - public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { - if (l1.getValue() > l2.getValue()) { - return -1; - } else if (l1.getValue().equals(l2.getValue())) { - return 0; - } else { - return 1; - } + widgets.getChildren().add(2, hiScoreTitle); + + if (isMultiGame) { + parseHiScore(score, this.game.getMyName().get()); + Multimedia.playSound("pling.wav"); + loadOnlineScores(); + this.isOnlineHiScore = false; + this.isLocalHiScore = false; + } else { + TextField usernameEntry = new TextField(); + usernameEntry.setPromptText("Enter username"); + usernameEntry.requestFocus(); + widgets.getChildren().add(3, usernameEntry); + + Button submitBtn = new Button("Submit"); + widgets.getChildren().add(4, submitBtn); + + usernameEntry.setOnKeyPressed(e -> { + if (e.getCode().equals(KeyCode.ENTER)) { + this.widgets.getChildren().remove(usernameEntry); + this.widgets.getChildren().remove(submitBtn); + + parseHiScore(score, usernameEntry.getText()); + + this.isOnlineHiScore = false; + this.isLocalHiScore = false; + + loadOnlineScores(); } }); - localList.getScores().bind(new SimpleListProperty<>(this.localScores)); - localList.reveal(); + + submitBtn.setOnAction(e -> { + this.widgets.getChildren().remove(usernameEntry); + this.widgets.getChildren().remove(submitBtn); + + parseHiScore(score, usernameEntry.getText()); + + this.isOnlineHiScore = false; + this.isLocalHiScore = false; + + loadOnlineScores(); + }); + Multimedia.playSound("pling.wav"); - }); + } } else { logger.info("User does not have a high score"); Text hiScoreTitle = new Text("High Scores"); hiScoreTitle.getStyleClass().add("title"); - widgets.getChildren().add(hiScoreTitle); + widgets.getChildren().add(2, hiScoreTitle); + startTimer(); + localList.reveal(); + onlineList.reveal(); + } + } + + public void parseHiScore(int score, String name) { + if (this.isLocalHiScore){ + logger.info("User has a local high score"); + this.localList.setName(name); + this.localScores.add(new Pair<>(name, score)); + this.localScores.sort(new Comparator<Pair<String, Integer>>() { + @Override + public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { + if (l1.getValue() > l2.getValue()) { + return -1; + } else if (l1.getValue().equals(l2.getValue())) { + return 0; + } else { + return 1; + } + } + }); + writeScores((List<Pair<String, Integer>>) localScores); + } - localList.getScores().bind(new SimpleListProperty<>(this.localScores)); + if (isOnlineHiScore) { + logger.info("User has an online high score"); + gameScore = new Pair<>(name, score); + this.onlineList.setName(name); + this.remoteScores.add(gameScore); + this.remoteScores.sort(new Comparator<Pair<String, Integer>>() { + @Override + public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { + if (l1.getValue() > l2.getValue()) { + return -1; + } else if (l1.getValue().equals(l2.getValue())) { + return 0; + } else { + return 1; + } + } + }); + if (!isMultiGame) { + writeOnlineScore(gameScore); + } + } + } + + public void receiveCommunication(String message) { + logger.info("Received online scores"); + String[] components = message.split(" "); + + if (components[0].equals("HISCORES")) { + parseOnlineScores(components[1]); + if (checkingForScores) { + checkHiScore(); + checkingForScores = false; + return; + } + startTimer(); localList.reveal(); + onlineList.reveal(); + } + } + + public void parseOnlineScores(String scores) { + logger.info("Loading online scores"); + onlineScores = new ArrayList<>(); + + String[] data = scores.split("\n"); + for (String nameScore : data) { + String[] values = nameScore.split(":"); + onlineScores.add(new Pair<>(values[0], Integer.parseInt(values[1]))); + } + + onlineScores.sort(new Comparator<Pair<String, Integer>>() { + @Override + public int compare(Pair<String, Integer> l1, Pair<String, Integer> l2) { + if (l1.getValue() > l2.getValue()) { + return -1; + } else if (l1.getValue().equals(l2.getValue())) { + return 0; + } else { + return 1; + } + } + }); + + this.remoteScores.clear(); + this.remoteScores.addAll(onlineScores); + } + + public void loadOnlineScores() { + this.communicator.send("HISCORES"); + } + + public void writeOnlineScore(Pair<String, Integer> pair) { + if (pair != null) { + this.communicator.send("HISCORE " + pair.getKey() + ":" + pair.getValue()); } } @@ -179,13 +325,23 @@ public class ScoresScene extends BaseScene{ logger.info("Loading scores from file"); ArrayList<Pair<String, Integer>> localScores = new ArrayList<>(); try{ + File f = new File("localscores.txt"); + if (!f.exists()) { + writeDefaultScores(); + } BufferedReader br = new BufferedReader(new FileReader("localscores.txt")); String line = br.readLine(); + int lineCounter = 0; while(line != null) { - String[] values = line.split(":"); - localScores.add(new Pair<>(values[0], Integer.parseInt(values[1]))); - line = br.readLine(); + lineCounter++; + if (lineCounter <= 10) { + String[] values = line.split(":"); + localScores.add(new Pair<>(values[0], Integer.parseInt(values[1]))); + line = br.readLine(); + } else { + break; + } } br.close(); @@ -209,7 +365,21 @@ public class ScoresScene extends BaseScene{ return localScores; } - public void writeScores(List<Pair<String, Integer>> nameScores) { + public static void writeDefaultScores() { + try { + logger.info("Writing default scores to file"); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("localscores.txt"))); + for (int i = 10; i > 0; i --) { + bw.write("test" + i + ":" + (1000*i)); + bw.newLine(); + } + bw.close(); + } catch (Exception e) { + logger.info("Error writing default scores to file: " + e.getMessage()); + } + } + + public static void writeScores(List<Pair<String, Integer>> nameScores) { logger.info("Writing scores to text file"); try { @@ -225,8 +395,25 @@ public class ScoresScene extends BaseScene{ } } + public void startTimer() { + logger.info("Starting highscore scene timer"); + timer = new Timer(); + TimerTask task = new TimerTask() { + @Override + public void run() { + Platform.runLater(() -> escapeToMenu()); + } + }; + + timer.schedule(task, 15000); + } + private void escapeToMenu() { Multimedia.stopSound(); + if (timer != null) { + timer.cancel(); + timer.purge(); + } this.gameWindow.startMenu(); } } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/GameWindow.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/GameWindow.java index be1214c0d6da456eb3915477441ddf1b0a43dcfa..18541f1a31f5ff1c974717d1042f28f27c385d0d 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/GameWindow.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/GameWindow.java @@ -60,7 +60,7 @@ public class GameWindow { communicator = new Communicator("ws://ofb-labs.soton.ac.uk:9700"); //Go to menu - startMenu(); + startLoadingScene(); } /** @@ -75,6 +75,9 @@ public class GameWindow { Font.loadFont(getClass().getResourceAsStream("/style/Orbitron-ExtraBold.ttf"), 32); } + public void startLoadingScene() { + loadScene(new LoadingScene(this)); + } /** * Display the main menu */ @@ -89,6 +92,14 @@ public class GameWindow { loadScene(new ChallengeScene(this)); } + public void startLobby() { + loadScene(new LobbyScene(this)); + } + + public void showOptions() { + loadScene(new OptionsScene(this)); + } + public void displayInstructions() { loadScene(new InstructionScene(this)); } @@ -97,6 +108,10 @@ public class GameWindow { loadScene(new ScoresScene(this, game)); } + public void startMultiplayerGame(String name) { + loadScene(new MultiplayerScene(this, name)); + } + /** * Setup the default settings for the stage itself (the window), such as the title and minimum width and height. */ diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/Leaderboard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/Leaderboard.java new file mode 100644 index 0000000000000000000000000000000000000000..59a43f1339be4cc7dad73c3582da0579ab67f644 --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/Leaderboard.java @@ -0,0 +1,31 @@ +package uk.ac.soton.comp1206.ui; + +import javafx.scene.text.Text; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +public class Leaderboard extends ScoresList { + private static final Logger logger = LogManager.getLogger(Leaderboard.class); + + public Leaderboard(String listName) { + super(listName); + setVisible(true); + } + + @Override + public void updateList() { + super.updateList(); + reveal(); + } + + public void setDead(String player) { + logger.info("Setting {} as dead", player); + for (Text nameScore : nameScores) { + if (nameScore.getText().startsWith(player)) { + deadPlayers.add(player); + } + } + updateList(); + } +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/ScoresList.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/ScoresList.java index af28a6ac90c94a01d2e593d090ea73ce1acff15c..847e02b8ac85aea5928a19857961cef1eb41b96a 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/ScoresList.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/ui/ScoresList.java @@ -1,9 +1,7 @@ package uk.ac.soton.comp1206.ui; -import javafx.animation.Animation; import javafx.animation.FadeTransition; import javafx.animation.SequentialTransition; -import javafx.animation.Transition; import javafx.beans.property.ListProperty; import javafx.beans.property.SimpleListProperty; import javafx.collections.ListChangeListener; @@ -24,10 +22,10 @@ public class ScoresList extends VBox { private static final Logger logger = LogManager.getLogger(ScoresList.class); public final SimpleListProperty<Pair<String, Integer>> scores = new SimpleListProperty(); - - private ArrayList<Text> nameScores = new ArrayList<>(); - - private String listName; + protected ArrayList<Text> nameScores = new ArrayList<>(); + protected String listName; + protected ArrayList<String> deadPlayers = new ArrayList<>(); + protected String name = ""; public ScoresList(String listName) { setVisible(false); @@ -39,6 +37,10 @@ public class ScoresList extends VBox { setSpacing(5); setAlignment(Pos.CENTER); + Text title = new Text(listName); + title.getStyleClass().add("heading"); + getChildren().add(title); + this.scores.addListener((ListChangeListener<? super Pair<String, Integer>>) e -> updateList()); } @@ -62,7 +64,12 @@ public class ScoresList extends VBox { Text nameScore = new Text(score.getKey() + ": " + score.getValue()); nameScore.setTextAlignment(TextAlignment.CENTER); nameScore.setFill(GameBlock.COLOURS[i]); - + if (this.deadPlayers.contains(score.getKey())) { + nameScore.getStyleClass().add("deadscore"); + } + if (this.name.equals(score.getKey())) { + nameScore.getStyleClass().add("myname"); + } nameScores.add(nameScore); getChildren().add(nameScore); } @@ -85,6 +92,11 @@ public class ScoresList extends VBox { sq.play(); } + public void setName(String name) { + logger.info("Name set to: {}", name); + this.name = name; + } + public ListProperty<Pair<String, Integer>> getScores() { return this.scores; } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/utility/Multimedia.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/utility/Multimedia.java index b504543fd565741821553953b92aab23ad5427db..e7d93bd3fc0be724f3daeeb14860c32c0f882344 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/utility/Multimedia.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/utility/Multimedia.java @@ -5,6 +5,7 @@ import javafx.scene.media.MediaPlayer; import javafx.util.Duration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import uk.ac.soton.comp1206.scene.OptionsScene; public class Multimedia { @@ -18,7 +19,7 @@ public class Multimedia { try { Media play = new Media(toPlay); backgroundPlayer = new MediaPlayer(play); - backgroundPlayer.setVolume(0.4); + backgroundPlayer.volumeProperty().bind(OptionsScene.getMusicLevel()); if (toLoop) { backgroundPlayer.setOnEndOfMedia(() -> backgroundPlayer.seek(Duration.ZERO)); } @@ -36,7 +37,7 @@ public class Multimedia { try { Media play = new Media(toPlay); mediaPlayer = new MediaPlayer(play); - mediaPlayer.setVolume(0.65); + mediaPlayer.volumeProperty().bind(OptionsScene.getSoundLevel()); mediaPlayer.play(); logger.info("Playing sound clip " + sound); } catch (Exception e) { diff --git a/tetrecs/src/main/resources/music/menu.mp3 b/tetrecs/src/main/resources/music/menu.mp3 deleted file mode 100644 index 0724b65ee300790b53ed7f4c79a800e934343feb..0000000000000000000000000000000000000000 Binary files a/tetrecs/src/main/resources/music/menu.mp3 and /dev/null differ diff --git a/tetrecs/src/main/resources/music/menuMusic.wav b/tetrecs/src/main/resources/music/menuMusic.wav new file mode 100644 index 0000000000000000000000000000000000000000..2647529951c66f4f4ae01acd99863950421dbf0c Binary files /dev/null and b/tetrecs/src/main/resources/music/menuMusic.wav differ diff --git a/tetrecs/src/main/resources/music/menumusic.mp3 b/tetrecs/src/main/resources/music/menumusic.mp3 deleted file mode 100644 index dfab6dd74b8cb907a454d968c6797a5d54e9fb60..0000000000000000000000000000000000000000 Binary files a/tetrecs/src/main/resources/music/menumusic.mp3 and /dev/null differ diff --git a/tetrecs/src/main/resources/style/game.css b/tetrecs/src/main/resources/style/game.css index 9eee303a2c77680d1a7eb381db628d104aa5544c..7eaa86d49f739d47afa78bf19dc24049935f01cc 100644 --- a/tetrecs/src/main/resources/style/game.css +++ b/tetrecs/src/main/resources/style/game.css @@ -69,6 +69,24 @@ -fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1); } +.lobbyItem { + -fx-fill: white; + -fx-font-family: 'Orbitron'; + -fx-font-size: 20px; + -fx-font-weight: 700; + -fx-border-color: black; + -fx-stroke: black; + -fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1); +} + +.lobbyItem:hover { + -fx-fill: yellow; +} + +.lobbyItem.selected { + -fx-fill: yellow; +} + .score { -fx-fill: yellow; -fx-font-family: 'Orbitron'; @@ -144,7 +162,7 @@ .channelItem { -fx-fill: white; -fx-font-family: 'Orbitron'; - -fx-font-size: 16px; + -fx-font-size: 18px; -fx-font-weight: 700; -fx-border-color: black; -fx-stroke: black; @@ -174,7 +192,7 @@ .messages { -fx-background-color: transparent; - -fx-font-size: 12px; + -fx-font-size: 13px; -fx-font-family: 'Orbitron'; } @@ -204,7 +222,7 @@ TextField { } .instructions { - -fx-font-size: 10px; + -fx-font-size: 14px; -fx-font-family: 'Orbitron'; -fx-fill: white; } \ No newline at end of file diff --git a/tetrecs/target/classes/music/menu.mp3 b/tetrecs/target/classes/music/menu.mp3 deleted file mode 100644 index 0724b65ee300790b53ed7f4c79a800e934343feb..0000000000000000000000000000000000000000 Binary files a/tetrecs/target/classes/music/menu.mp3 and /dev/null differ diff --git a/tetrecs/target/classes/style/game.css b/tetrecs/target/classes/style/game.css index 9eee303a2c77680d1a7eb381db628d104aa5544c..7eaa86d49f739d47afa78bf19dc24049935f01cc 100644 --- a/tetrecs/target/classes/style/game.css +++ b/tetrecs/target/classes/style/game.css @@ -69,6 +69,24 @@ -fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1); } +.lobbyItem { + -fx-fill: white; + -fx-font-family: 'Orbitron'; + -fx-font-size: 20px; + -fx-font-weight: 700; + -fx-border-color: black; + -fx-stroke: black; + -fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1); +} + +.lobbyItem:hover { + -fx-fill: yellow; +} + +.lobbyItem.selected { + -fx-fill: yellow; +} + .score { -fx-fill: yellow; -fx-font-family: 'Orbitron'; @@ -144,7 +162,7 @@ .channelItem { -fx-fill: white; -fx-font-family: 'Orbitron'; - -fx-font-size: 16px; + -fx-font-size: 18px; -fx-font-weight: 700; -fx-border-color: black; -fx-stroke: black; @@ -174,7 +192,7 @@ .messages { -fx-background-color: transparent; - -fx-font-size: 12px; + -fx-font-size: 13px; -fx-font-family: 'Orbitron'; } @@ -204,7 +222,7 @@ TextField { } .instructions { - -fx-font-size: 10px; + -fx-font-size: 14px; -fx-font-family: 'Orbitron'; -fx-fill: white; } \ No newline at end of file diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class b/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class index f240a5fb067647a6e97467e55deccf97ca0b1c87..f0d85b6d2071a4fce55b16b5054c42edf2566ea3 100644 Binary files a/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class and b/tetrecs/target/classes/uk/ac/soton/comp1206/game/Game.class differ diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/game/GamePiece.class b/tetrecs/target/classes/uk/ac/soton/comp1206/game/GamePiece.class index 580673cfa6c805087cf2419b840265615cd04929..d26528c24acac20afdd51a29bf9c9d9407242059 100644 Binary files a/tetrecs/target/classes/uk/ac/soton/comp1206/game/GamePiece.class and b/tetrecs/target/classes/uk/ac/soton/comp1206/game/GamePiece.class differ diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class index 190abd750f56f6ec1d22c4f9aeec33d970d691b1..ad0c9db41e584f034dae888bb8ee27054e6fcf6f 100644 Binary files a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class and b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/ChallengeScene.class differ diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class index 5a5424bebb75fe9dda472b832ce79f17ba0d6c4e..7453b0dfc02dd676f8cbeffd489f15aef967de33 100644 Binary files a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class and b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class differ diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/ui/GameWindow.class b/tetrecs/target/classes/uk/ac/soton/comp1206/ui/GameWindow.class index f433bc570a1fa58d5eb8578afa8bd40e23ad84dd..8521ce32017ef17e94b77b506033a31d869ed4c6 100644 Binary files a/tetrecs/target/classes/uk/ac/soton/comp1206/ui/GameWindow.class and b/tetrecs/target/classes/uk/ac/soton/comp1206/ui/GameWindow.class differ