Nearly finished

This commit is contained in:
alexander 2024-04-26 13:34:39 +01:00
parent 313acdb910
commit 7b75020f6b
22 changed files with 986 additions and 249 deletions

View File

@ -6,6 +6,7 @@ import javafx.scene.layout.GridPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.event.BlockClickedListener;
import uk.ac.soton.comp1206.event.BlockRotate;
import uk.ac.soton.comp1206.game.GamePiece;
import uk.ac.soton.comp1206.game.Grid;
@ -58,9 +59,10 @@ public class GameBoard extends GridPane {
*/
private BlockClickedListener blockClickedListener;
int keyBoardX = 0;
int keyBoardY = 0;
private boolean keyboardOn = false;
private int keyBoardX = 0;
private int keyBoardY = 0;
private BlockRotate blockRotate;
/**
* Create a new GameBoard, based off a given grid, with a visual width and height.
@ -159,12 +161,18 @@ public class GameBoard extends GridPane {
if(event.getButton() == MouseButton.PRIMARY) {
this.blockClicked(event, block);
} else if(event.getButton() == MouseButton.SECONDARY) {
logger.info("Not yet implemented");
this.blockRotate.rotate();
}
});
block.setOnMouseEntered(event -> {
this.clearHover();
keyboardOn = false;
block.paintHover();
keyBoardX = block.getX();
keyBoardY = block.getY();
});
block.setOnMouseExited(event -> {
@ -195,14 +203,25 @@ public class GameBoard extends GridPane {
}
}
/**
* Clears the grid
*/
public void clear() {
this.grid.clear();
}
/**
* Adds piece to game board
* @param piece the piece to add
*/
public void addPiece(GamePiece piece) {
this.grid.addPiece(piece, 1, 1);
}
/**
* Set a piece on the board
* @param piece piece
*/
public void setPiece(GamePiece piece) {
for(int[] gameBlock: piece.getBlocks()) {
this.clear();
@ -211,9 +230,77 @@ public class GameBoard extends GridPane {
}
}
public void setCentreFill() {
/**
* Handles keyboard controls
* @param direction
*/
public void keyboardInput(String direction){
keyboardOn = true;
this.clearHover();
switch (direction){
case "up":
if (keyBoardY > 0) {
keyBoardY--;
}
break;
case "down":
if (keyBoardY < blocks[keyBoardX].length - 1) {
keyBoardY++;
}
break;
case "left":
if (keyBoardX > 0) {
keyBoardX--;
}
break;
case "right":
if (keyBoardX < blocks.length - 1) {
keyBoardX++;
}
break;
default:
// Optionally handle unexpected input
break;
}
// Ensure the new coordinates are within the bounds of the array
keyBoardX = Math.max(0, Math.min(keyBoardX, blocks.length - 1));
keyBoardY = Math.max(0, Math.min(keyBoardY, blocks[keyBoardX].length - 1));
this.blocks[keyBoardX][keyBoardY].paintHover();
}
/**
* Removes hover effect from board
*/
public void clearHover() {
for (int i = 0; i < this.cols; i++) {
for (int j = 0; j < this.rows; j++) {
this.blocks[i][j].paint();
}
}
}
/**
* Set the center fill
*/
public void setCenterFill() {
this.blocks[this.cols/2][this.rows/2].setPaintCentre(true);
}
/**
* Enables keyboard highlighting
*/
public void keyboardSelect() {
this.blockClicked(null, this.getBlock(keyBoardX, keyBoardY));
}
/**
* set the block rotation
* @param blockRotate
*/
public void setBlockRotate(BlockRotate blockRotate) {
this.blockRotate = blockRotate;
}
}

View File

@ -11,13 +11,19 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.scene.ScoresScene;
/**
* Represents a visual leaderboard component in a vertical box layout.
*/
public class Leaderboard extends VBox {
Logger logger = LogManager.getLogger(ScoresScene.class);
private SimpleListProperty<Pair<String, Integer>> leaderboard;
/**
* Creates a new Leaderboard component.
*
* @param leaderboard The data model for leaderboard entries, where each entry is a pair of player name and score.
*/
public Leaderboard(SimpleListProperty<Pair<String, Integer>> leaderboard) {
this.leaderboard = leaderboard;
this.leaderboard.addListener((ListChangeListener<Pair<String, Integer>>) c -> {
@ -25,14 +31,21 @@ public class Leaderboard extends VBox {
});
}
/**
* Updates the visual display of the leaderboard when changes occur in the data model.
*
* @param change Details about the change that occurred in the leaderboard data.
*/
private void updateLeaderBoard(ListChangeListener.Change<? extends Pair<String, Integer>> change) {
logger.info("Updating leaderboard");
if (change.next()) {
if (change.wasPermutated() || change.wasReplaced() || change.wasRemoved()) {
super.getChildren().clear();
}
if (change.wasAdded()) {
for (Pair<String, Integer> pair : change.getAddedSubList()) {
var text = new Text(pair.getKey() + ": " + pair.getValue());
@ -42,6 +55,4 @@ public class Leaderboard extends VBox {
}
}
}
}

View File

@ -0,0 +1,190 @@
package uk.ac.soton.comp1206.component;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.Background;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.event.StartMultiplayer;
import uk.ac.soton.comp1206.tools.MultiplayerGame;
/**
* The LobbyInterface class provides a user interface within the lobby of a multiplayer game.
* It allows users to interact with the game by joining, creating, and leaving games.
*/
public class LobbyInterface extends BorderPane {
private static final Logger logger = LogManager.getLogger(LobbyInterface.class);
private VBox vbox = new VBox();
private Text title;
private HBox hbox = new HBox();
private ListView<String> playerList;
private ListView<String> messages;
private Button leaveButton = new Button("Leave");
private MultiplayerGame multiplayerGame;
private SimpleStringProperty newGame;
private TextField newGameName;
private TextField chatBox;
private StartMultiplayer startMultiplayer;
private VBox messageBox = new VBox();
private HBox buttonBox = new HBox();
/**
* Constructs a new LobbyInterface with the given multiplayer game context.
*
* @param multiplayerGame the multiplayer game instance this interface will interact with
*/
public LobbyInterface(MultiplayerGame multiplayerGame) {
super();
this.multiplayerGame = multiplayerGame;
title = new Text();
title.getStyleClass().add("heading");
vbox.getChildren().add(title);
hbox.getChildren().add(messageBox);
leaveButton.getStyleClass().add("small-button");
messages = new ListView<>(multiplayerGame.getMessages());
messages.setMinWidth(400);
messageBox.getChildren().add(messages);
chatBox = new TextField();
messageBox.getChildren().add(chatBox);
playerList = new ListView<>(multiplayerGame.getPlayers());
playerList.setBackground(Background.fill(Color.TRANSPARENT));
hbox.getChildren().add(playerList);
// Custom factory cell for player list
playerList.setCellFactory(lv -> new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
getStyleClass().clear();
} else {
setText(item);
if (!getStyleClass().contains("player-item")) {
getStyleClass().add("player-item");
}
}
}
});
messages.setCellFactory(lv -> new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
getStyleClass().clear();
} else {
setText(item);
if (!getStyleClass().contains("message-item")) {
getStyleClass().add("message-item");
}
}
}
});
leaveButton.setOnAction(event -> {
multiplayerGame.leave();
});
multiplayerGame.nameProperty().addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> {
updateUI(multiplayerGame.getName());
});
});
this.setTop(vbox);
this.setBottom(null);
this.updateUI("");
}
/**
* Updates the UI elements based on the current game state.
*
* @param gameName the name of the current game, if any
*/
private void updateUI(String gameName) {
if (!gameName.isEmpty()) {
vbox.getChildren().clear();
title.setText("Current game: " + gameName);
vbox.getChildren().add(title);
if (!vbox.getChildren().contains(hbox)) {
vbox.getChildren().add(hbox);
}
vbox.getChildren().add(buttonBox);
buttonBox.getChildren().clear();
buttonBox.getChildren().add(leaveButton);
} else {
title.setText("Not currently in a game.");
vbox.getChildren().remove(hbox);
vbox.getChildren().clear();
var createGame = new Text("Create Game");
createGame.getStyleClass().add("heading");
vbox.getChildren().add(createGame);
newGameName = new TextField();
vbox.getChildren().add(newGameName);
this.setBottom(null);
}
if (this.multiplayerGame.isHostProperty().get()) {
var startGame = new Button("Start Game");
startGame.getStyleClass().add("small-button");
startGame.setOnAction(event -> {
this.startMultiplayer.start();
});
buttonBox.getChildren().clear();
var spacer = new Rectangle();
spacer.setWidth(15);
buttonBox.getChildren().addAll(startGame, spacer, leaveButton);
} else {
buttonBox.getChildren().removeIf(child -> child instanceof Button && "Start Game".equals(((Button) child).getText()));
}
}
/**
* Retrieves the entered name for a new game.
* @return a string representing the new game name
*/
public String getNextGameName() {
return newGameName.getText();
}
/**
* Handles the action triggered when the Enter key is pressed.
*/
public void callOnEnter() {
logger.info("Enter clicked");
if (this.multiplayerGame.getName().isEmpty()) {
logger.info("Creating new game");
this.multiplayerGame.createGame(newGameName.getText());
} else {
this.multiplayerGame.sendMessage(chatBox.getText());
chatBox.setText("");
}
}
/**
* Sets the action to be taken when the multiplayer game starts.
* @param startMultiplayer the start multiplayer event handler
*/
public void setOnStartMultiplayer(StartMultiplayer startMultiplayer) {
this.startMultiplayer = startMultiplayer;
}
}

View File

@ -1,44 +0,0 @@
package uk.ac.soton.comp1206.component;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
import uk.ac.soton.comp1206.state.MultiplayerGame;
public class LobyInterface extends BorderPane {
private VBox vbox = new VBox();
public LobyInterface(MultiplayerGame multiplayerGame) {
super();
var title = new Text(multiplayerGame.getName());
title.getStyleClass().add("heading");
super.setTop(vbox);
multiplayerGame.nameProperty().addListener((observable, oldValue, newValue) -> {
title.setText("Current game: " + newValue);
var leaveButton = new Button("Leave");
super.setBottom(leaveButton);
leaveButton.setOnAction(event -> {
multiplayerGame.leave();
});
var messageBox = new HBox();
vbox.getChildren().add(messageBox);
var textInput = new TextField();
messageBox.getChildren().add(textInput);
});
vbox.getChildren().add(title);
}
}

View File

@ -12,13 +12,20 @@ import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.event.ChannelSelected;
import uk.ac.soton.comp1206.scene.MenuScene;
/**
* The MultiplayerGameList class represents a visual list of multiplayer game channels.
* It provides functionality to select a game channel from the list.
*/
public class MultiplayerGameList extends VBox {
private static final Logger logger = LogManager.getLogger(MenuScene.class);
private static final Logger logger = LogManager.getLogger(MultiplayerGameList.class);
private SimpleListProperty<String> channels;
private ChannelSelected channelSelected;
/**
* Constructs a new MultiplayerGameList with the given list of game channels.
*
* @param channels the observable list of game channel names
*/
public MultiplayerGameList(SimpleListProperty<String> channels) {
super();
this.channels = channels;
@ -31,7 +38,7 @@ public class MultiplayerGameList extends VBox {
channels.addListener((ListChangeListener<String>) c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(channel -> {renderChannel(channel);});
c.getAddedSubList().forEach(this::renderChannel);
} else {
reset();
}
@ -39,33 +46,48 @@ public class MultiplayerGameList extends VBox {
});
}
/**
* Sets the event handler to be called when a game channel is selected.
*
* @param channelSelected the event handler for channel selection
*/
public void setOnChannelSelected(ChannelSelected channelSelected) {
this.channelSelected = channelSelected;
}
/**
* Initializes the list of game channels by rendering each channel as a button.
*/
private void initializeChannelList() {
channels.forEach(this::renderChannel);
}
/**
* Renders a button for each channel in the game list.
*
* @param channel the name of the game channel to render as a button
*/
private void renderChannel(String channel) {
Platform.runLater(() -> {
var channelButton = new Button(channel);
channelButton.getStyleClass().add("channel-item");
channelButton.setOnAction(event -> {
logger.info("Button pressed "+ channel);
this.channelSelected.channelSelected(channel);
logger.info("Button pressed " + channel);
if (channelSelected != null) {
channelSelected.channelSelected(channel);
}
});
this.getChildren().add(channelButton);
});
}
/**
* Resets the list by removing all buttons. This is typically called when the underlying data model changes significantly.
*/
private void reset() {
Platform.runLater(() -> {
this.getChildren().removeIf(node -> node instanceof Button);
});
}
}

View File

@ -5,74 +5,122 @@ import javafx.scene.media.MediaPlayer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
/**
* The SoundPlayer class provides static methods to manage audio playback, including sound effects and background music.
* This class is not intended to be instantiated.
*/
public class SoundPlayer {
private static final Logger logger = LogManager.getLogger(SoundPlayer.class);
private static MediaPlayer mediaPlayer;
private static MediaPlayer secondaryMediaPlayer;
private static int mainVolume = 100; // Default volume percentage for main sounds
private static int secondaryVolume = 100; // Default volume percentage for background music
private static MediaPlayer introPlayer;
private static MediaPlayer loopPlayer;
private static boolean audioEnabled = true;
/**
* Private constructor to prevent instantiation of this utility class.
*/
private SoundPlayer() {
// Private constructor to prevent instantiation
}
/**
* Plays a single audio file once.
*
* @param file the path to the audio file to play
*/
public static void playAudio(String file) {
if (!audioEnabled) return;
public static void playSound(String filePath) {
try {
String toPlay = SoundPlayer.class.getResource(filePath).toExternalForm();
Media play = new Media(toPlay);
mediaPlayer = new MediaPlayer(play);
mediaPlayer.setVolume(mainVolume);
String toPlay = SoundPlayer.class.getResource(file).toExternalForm();
Media media = new Media(toPlay);
MediaPlayer mediaPlayer = new MediaPlayer(media);
mediaPlayer.play();
} catch (Exception e) {
e.printStackTrace();
logger.error("Unable to play audio file, disabling audio");
audioEnabled = false;
logger.error("Unable to play audio file: " + file + ", disabling audio", e);
}
}
private static Media createMedia(String mediaUrl) {
return new Media(mediaUrl);
}
/**
* Plays a looping background music file.
*
* @param loopFile the path to the audio file to loop
*/
public static void playBackgroundMusic(String loopFile) {
if (!audioEnabled) return;
public static void playBackgroundMusic(String fileName) {
try {
logger.info("Playing background music: " + fileName);
Media media = new Media(fileName);
if (secondaryMediaPlayer != null) {
secondaryMediaPlayer.stop(); // Stop current playing music if any
}
secondaryMediaPlayer = new MediaPlayer(media);
secondaryMediaPlayer.setVolume(secondaryVolume / 100.0);
secondaryMediaPlayer.setCycleCount(MediaPlayer.INDEFINITE); // Loop indefinitely
secondaryMediaPlayer.play();
String mediaUrl = SoundPlayer.class.getResource(loopFile).toExternalForm();
Media media = new Media(mediaUrl);
loopPlayer = new MediaPlayer(media);
loopPlayer.setCycleCount(MediaPlayer.INDEFINITE);
loopPlayer.play();
} catch (Exception e) {
logger.error("Error playing background music: " + fileName, e);
audioEnabled = false;
logger.error("Unable to play background music: " + loopFile + ", disabling audio", e);
}
}
public static void stopSound() {
if (mediaPlayer != null) {
mediaPlayer.stop();
/**
* Plays a one-time intro audio followed by a looping background music file.
*
* @param introFile the path to the intro audio file
* @param loopFile the path to the looping background music file
*/
public static void playBackgroundMusic(String introFile, String loopFile) {
if (!audioEnabled) return;
try {
// Play intro
String introMediaUrl = SoundPlayer.class.getResource(introFile).toExternalForm();
Media introMedia = new Media(introMediaUrl);
introPlayer = new MediaPlayer(introMedia);
// Prepare loop
String loopMediaUrl = SoundPlayer.class.getResource(loopFile).toExternalForm();
Media loopMedia = new Media(loopMediaUrl);
loopPlayer = new MediaPlayer(loopMedia);
loopPlayer.setCycleCount(MediaPlayer.INDEFINITE);
// When the intro finishes, start the loop
introPlayer.setOnEndOfMedia(() -> {
introPlayer.dispose(); // Release the intro player resources
loopPlayer.play();
});
introPlayer.play();
} catch (Exception e) {
audioEnabled = false;
logger.error("Unable to play background music with intro: " + introFile + ", disabling audio", e);
}
}
/**
* Stops all background music and releases associated resources.
*/
public static void stopBackgroundMusic() {
if (secondaryMediaPlayer != null) {
secondaryMediaPlayer.stop();
if (introPlayer != null) {
introPlayer.stop();
introPlayer.dispose(); // Properly release resources
introPlayer = null;
}
if (loopPlayer != null) {
loopPlayer.stop();
loopPlayer.dispose(); // Properly release resources
loopPlayer = null;
}
}
public static void setMainVolume(int volume) {
mainVolume = volume;
if (mediaPlayer != null) {
mediaPlayer.setVolume(mainVolume / 100.0);
}
}
public static void setBackgroundMusicVolume(int volume) {
secondaryVolume = volume;
if (secondaryMediaPlayer != null) {
secondaryMediaPlayer.setVolume(secondaryVolume / 100.0);
/**
* Toggles the state of audio enabled/disabled.
* If disabling, stops any playing music.
*/
public static void toggleAudio() {
audioEnabled = !audioEnabled;
if (!audioEnabled) {
stopBackgroundMusic();
} else {
logger.info("Audio enabled");
}
}
}

View File

@ -0,0 +1,8 @@
package uk.ac.soton.comp1206.event;
public interface BlockRotate {
/**
* Rotate the next piece
*/
public void rotate();
}

View File

@ -1,5 +1,9 @@
package uk.ac.soton.comp1206.event;
public interface ChatMessage {
/**
* Chat message event
* @param message
*/
public void getMessage(String message);
}

View File

@ -0,0 +1,8 @@
package uk.ac.soton.comp1206.event;
public interface StartMultiplayer {
/**
* Used to start multiplayer
*/
public void start();
}

View File

@ -6,6 +6,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.component.GameBlock;
import uk.ac.soton.comp1206.component.GameBoard;
import uk.ac.soton.comp1206.effects.SoundPlayer;
import uk.ac.soton.comp1206.event.GameLoopHandler;
import uk.ac.soton.comp1206.event.LooseALife;
import uk.ac.soton.comp1206.event.OutOfLives;
@ -47,6 +48,7 @@ public class Game {
private IntegerProperty lives = new SimpleIntegerProperty(3);
private IntegerProperty score = new SimpleIntegerProperty(0);
private IntegerProperty level = new SimpleIntegerProperty(0);
private IntegerProperty multiplier = new SimpleIntegerProperty(1);
protected GamePiece currentPiece;
protected GamePiece nextPiece;
@ -72,8 +74,6 @@ public class Game {
this.grid = new Grid(cols,rows);
}
/**
@ -100,6 +100,9 @@ public class Game {
this.startGameLoop();
score.addListener((observable, oldValue, newValue) -> {
level.set(newValue.intValue() / 1000);
});
}
@ -126,8 +129,10 @@ public class Game {
*/
if (grid.canPlayPiece(currentPiece, x, y)) {
SoundPlayer.playAudio("/sounds/place.wav");
logger.info("Piece can be placed");
} else {
SoundPlayer.playAudio("/sounds/fail.wav");
logger.info("Piece cannot be placed");
return;
}
@ -182,14 +187,30 @@ public class Game {
}
/**
*
* Increment the next pieces
*/
public void incrementNextPieces() {
this.score.set(this.score.get() + grid.clearLines());
int[] cleared = this.grid.clearLines();
if (cleared[0] > 0) {
// Calculate score using the number of blocks cleared and the multiplier
int scoreIncrement = cleared[0] * cleared[1] * 10 * multiplier.get();
score.set(score.get() + scoreIncrement);
// Increment the multiplier for each line clearing event
multiplier.set(multiplier.get() + 1);
} else {
// Reset multiplier if no lines are cleared
multiplier.set(1);
}
// Move to the next piece
this.currentPiece = this.nextPiece;
this.nextPiece = this.spawnPiece();
}
/**
* Get current score
* @return InterProperty score
@ -267,6 +288,9 @@ public class Game {
this.executor.shutdownNow();
}
/**
* Game loop handles the executing of the game
*/
private void gameLoop() {
logger.info("Game loop");
this.nextLoop.cancel(true);
@ -314,4 +338,5 @@ public class Game {
this.looseALife = looseALife;
}
}

View File

@ -5,6 +5,7 @@ import javafx.beans.property.SimpleIntegerProperty;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.effects.SoundPlayer;
import java.util.HashSet;
@ -32,6 +33,7 @@ public class Grid {
*/
private final int rows;
/**
* The grid is a 2D arrow with rows and columns of SimpleIntegerProperties.
*/
@ -161,55 +163,64 @@ public class Grid {
/**
* Checks which lines can be cleared
* @return number of blocks cleared
* @return array of number of lines and blocks
*/
public int clearLines() {
//List of cords to clear
public int[] clearLines() {
HashSet<int[]> cordsToRemove = new HashSet<>();
int xLines = 0;
int yLines = 0;
//Count and clear lines in the x-axis
for(int x = 0; x < cols; x++) {
// Count and clear lines in the x-axis (columns)
for (int x = 0; x < cols; x++) {
boolean isLine = true;
for(int y = 0; y < rows; y++) {
for (int y = 0; y < rows; y++) {
if (this.get(x, y) == 0) {
isLine = false;
break;
}
}
if(isLine) {
xLines ++;
if (isLine) {
xLines++;
for (int y = 0; y < rows; y++) {
cordsToRemove.add(new int[]{x, y});
}
}
}
//Count and clear in the y-axis
for(int y = 0; y < rows; y++) {
// Count and clear lines in the y-axis (rows)
for (int y = 0; y < rows; y++) {
boolean isLine = true;
for(int x = 0; x < cols; x++) {
for (int x = 0; x < cols; x++) {
if (this.get(x, y) == 0) {
isLine = false;
break;
}
}
if(isLine) {
xLines ++;
for (int x = 0; x < rows; x++) {
if (isLine) {
yLines++;
for (int x = 0; x < cols; x++) {
cordsToRemove.add(new int[]{x, y});
}
}
}
for(int[] cords : cordsToRemove) {
grid[cords[0]][cords[1]].set(0);
// Clear blocks from grid
for (int[] cords : cordsToRemove) {
this.set(cords[0], cords[1], 0);
}
// Play sound if any line was cleared
if (!cordsToRemove.isEmpty()) {
SoundPlayer.playAudio("/sounds/clear.wav");
}
int totalLinesCleared = xLines + yLines;
int totalBlocksCleared = cordsToRemove.size();
return cordsToRemove.size();
return new int[] {totalBlocksCleared, totalLinesCleared};
}
/**
* Clears the whole board
*/

View File

@ -28,11 +28,11 @@ import uk.ac.soton.comp1206.ui.GameWindow;
*/
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 Timer timer;
protected GameBoard board;
protected GameBoard nextPiece;
protected GameBoard nextPiece2;
@ -66,7 +66,7 @@ public class ChallengeScene extends BaseScene {
var mainPane = new BorderPane();
challengePane.getChildren().add(mainPane);
var board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2);
board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2);
mainPane.setCenter(board);
//Right side formatting
@ -154,7 +154,7 @@ public class ChallengeScene extends BaseScene {
//Next piece grid
this.nextPiece = new GameBoard(3, 3, this.gameWindow.getWidth()/6, this.gameWindow.getWidth()/6);
this.nextPiece.setCentreFill();
this.nextPiece.setCenterFill();
this.nextPiece.setPadding(new Insets(20,20,20,20));
rightBox.getChildren().add(nextPiece);
@ -191,9 +191,7 @@ public class ChallengeScene extends BaseScene {
this.game.setOnGameLoop(this::gameLoop);
this.game.setOnOutOfLives(this::gameOver);
this.game.setOnLossALife(this::lossOfLife);
this.board.setBlockRotate(this::rotateNext);
}
@ -201,7 +199,6 @@ public class ChallengeScene extends BaseScene {
* Handling loss of life events
*/
private void lossOfLife() {
SoundPlayer.playSound("sounds/lifeloose.wav");
this.game.incrementNextPieces();
this.updateNextPieces();
}
@ -210,6 +207,7 @@ public class ChallengeScene extends BaseScene {
* When the game is over
*/
private void gameOver() {
SoundPlayer.playAudio("/sounds/fail.wav");
//This is required so that it runs on the javaFX thread and not in the gameLoop thread
Platform.runLater(() -> {
logger.info("Game Over");
@ -280,6 +278,8 @@ public class ChallengeScene extends BaseScene {
@Override
public void initialise() {
SoundPlayer.stopBackgroundMusic();
SoundPlayer.playBackgroundMusic("/music/game_start.wav","/music/game.wav");
logger.info("Initialising Challenge");
game.start();
@ -302,13 +302,22 @@ public class ChallengeScene extends BaseScene {
}
});
this.game.getLives().addListener((observable, oldValue, newValue) -> {
if(oldValue.intValue() > newValue.intValue()) {
SoundPlayer.playAudio("/sounds/lifelose.wav");
} else {
}
});
}
private void endGame() {
logger.info("Ending game");
this.game.killGameLoop();
//SoundPlayer.stopAll();
SoundPlayer.stopBackgroundMusic();
SoundPlayer.playBackgroundMusic("/music/end.wav");
}
@ -324,6 +333,7 @@ public class ChallengeScene extends BaseScene {
* Swap the next pieces in the queue
*/
public void swampNextPieces() {
SoundPlayer.playAudio("/sounds/place.wav");
this.game.swapNextPieces();
this.updateNextPieces();
}
@ -332,6 +342,7 @@ public class ChallengeScene extends BaseScene {
* Rotates the piece that is next up
*/
public void rotateNext() {
SoundPlayer.playAudio("/sounds/rotate.wav");
this.game.getCurrentGamePiece().rotate();
this.updateNextPieces();
}
@ -343,19 +354,29 @@ public class ChallengeScene extends BaseScene {
public void handleKeyPressed(KeyEvent keyEvent) {
logger.info("Handling key pressed");
if(keyEvent.getCode() == KeyCode.SPACE || keyEvent.getCode() == KeyCode.R) {
this.game.swapNextPieces();
this.updateNextPieces();
this.swampNextPieces();
} else if(keyEvent.getCode() == KeyCode.ENTER) {
this.board.keyboardSelect();
} else if(keyEvent.getCode() == KeyCode.Q || keyEvent.getCode() == KeyCode.OPEN_BRACKET || keyEvent.getCode() == KeyCode.Z) {
logger.info("Rotating left");
this.game.getCurrentGamePiece().rotate();
this.updateNextPieces();
return;
} else if(keyEvent.getCode() == KeyCode.E || keyEvent.getCode() == KeyCode.C || keyEvent.getCode() == KeyCode.CLOSE_BRACKET);
this.rotateNext();
} else if(keyEvent.getCode() == KeyCode.E || keyEvent.getCode() == KeyCode.C || keyEvent.getCode() == KeyCode.CLOSE_BRACKET) {
logger.info("Rotating right");
SoundPlayer.playAudio("/sounds/rotate.wav");
this.game.getCurrentGamePiece().rotate(3);
this.updateNextPieces();
} else if (keyEvent.getCode() == KeyCode.UP || keyEvent.getCode() == KeyCode.W) {
this.board.keyboardInput("up");
} else if (keyEvent.getCode() == KeyCode.DOWN || keyEvent.getCode() == KeyCode.S) {
logger.info("Moving down");
this.board.keyboardInput("down");
} else if (keyEvent.getCode() == KeyCode.LEFT || keyEvent.getCode() == KeyCode.A) {
logger.info("Moving left");
this.board.keyboardInput("left");
} else if (keyEvent.getCode() == KeyCode.RIGHT || keyEvent.getCode() == KeyCode.D) {
logger.info("Moving right");
this.board.keyboardInput("right");
}
}
}

View File

@ -2,15 +2,18 @@ package uk.ac.soton.comp1206.scene;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import uk.ac.soton.comp1206.component.LobyInterface;
import uk.ac.soton.comp1206.component.LobbyInterface;
import uk.ac.soton.comp1206.component.MultiplayerGameList;
import uk.ac.soton.comp1206.effects.SoundPlayer;
import uk.ac.soton.comp1206.network.Communicator;
import uk.ac.soton.comp1206.ui.GamePane;
import uk.ac.soton.comp1206.ui.GameWindow;
@ -22,6 +25,9 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Represents the lobby scene where players can join multiplayer games.
*/
public class LobbyScene extends BaseScene {
private static final Logger logger = LogManager.getLogger(LobbyScene.class);
@ -30,14 +36,23 @@ public class LobbyScene extends BaseScene {
private SimpleListProperty<String> gamesList = new SimpleListProperty<>(FXCollections.observableArrayList());
private MultiplayerGameList multiplayerGameList = new MultiplayerGameList(gamesList);
private ScheduledFuture<?> nextLoop;
private LobyInterface lobyInterface;
private LobbyInterface lobbyInterface;
private HBox hBox;
private SimpleStringProperty simpleStringProperty;
/**
* Constructs a new LobbyScene.
*
* @param gameWindow The main game window.
*/
public LobbyScene(GameWindow gameWindow) {
super(gameWindow);
lobyInterface = new LobyInterface(gameWindow.getMultiplayerGame());
lobbyInterface = new LobbyInterface(gameWindow.getMultiplayerGame());
}
/**
* Initialises the scene, setting up communications and event listeners.
*/
@Override
public void initialise() {
communicator = gameWindow.getCommunicator();
@ -46,10 +61,14 @@ public class LobbyScene extends BaseScene {
multiplayerGameList.setOnChannelSelected(this::handleChannelSelected);
scene.setOnKeyPressed(e -> {
if(this.gameWindow.getMultiplayerGame().getName() != ""){
this.gameWindow.getMultiplayerGame().leave();
if (e.getCode() == KeyCode.ESCAPE) {
if (!this.gameWindow.getMultiplayerGame().getName().isEmpty()) {
this.gameWindow.getMultiplayerGame().leave();
}
this.gameWindow.startMenu();
} else if (e.getCode() == KeyCode.ENTER) {
this.lobbyInterface.callOnEnter();
}
this.gameWindow.startMenu();
});
gameWindow.getMultiplayerGame().nameProperty().addListener((observable, oldValue, newValue) -> {
@ -59,8 +78,20 @@ public class LobbyScene extends BaseScene {
showChannelSelector();
}
});
lobbyInterface.setOnStartMultiplayer(this::startGame);
}
/**
* Starts a multiplayer game.
*/
private void startGame() {
gameWindow.startMultiplayerGame();
}
/**
* Constructs the visual elements of the lobby scene.
*/
@Override
public void build() {
logger.info("Building Lobby");
@ -79,24 +110,31 @@ public class LobbyScene extends BaseScene {
BorderPane.setAlignment(title, Pos.CENTER);
mainPane.setTop(title);
renderUI();
}
/**
* Hides the channel selector and shows the lobby interface.
*/
private void hideChannelSelector() {
hBox.getChildren().clear();
hBox.getChildren().add(lobyInterface);
lobyInterface.setMinWidth(gameWindow.getWidth() * 0.9);
lobyInterface.setMaxHeight(gameWindow.getHeight() * 0.8);
hBox.getChildren().add(lobbyInterface);
lobbyInterface.setMinWidth(gameWindow.getWidth() * 0.9);
lobbyInterface.setMaxHeight(gameWindow.getHeight() * 0.8);
}
/**
* Shows the channel selector.
*/
private void showChannelSelector() {
logger.info("Showing channel selector");
hBox.getChildren().clear();
renderUI();
}
/**
* Renders the user interface components inside the lobby.
*/
private void renderUI() {
hBox.setPadding(new Insets(100, 30, 0, 30));
hBox.getChildren().add(multiplayerGameList);
@ -107,12 +145,17 @@ public class LobbyScene extends BaseScene {
spacer.setWidth(gameWindow.getWidth() * 0.02);
hBox.getChildren().add(spacer);
hBox.getChildren().add(lobyInterface);
lobyInterface.getStyleClass().add("multiplayer-game-list");
lobyInterface.setMaxHeight(gameWindow.getHeight() * 0.8);
lobyInterface.setMinWidth(gameWindow.getWidth() * 0.5);
hBox.getChildren().add(lobbyInterface);
lobbyInterface.getStyleClass().add("multiplayer-game-list");
lobbyInterface.setMaxHeight(gameWindow.getHeight() * 0.8);
lobbyInterface.setMinWidth(gameWindow.getWidth() * 0.5);
}
/**
* Handles network messages related to multiplayer game listings.
*
* @param response The network message received.
*/
private void networkHandler(String response) {
try {
logger.info("Message received");
@ -131,6 +174,9 @@ public class LobbyScene extends BaseScene {
}
}
/**
* Sends a request to list available multiplayer games and schedules the next send.
*/
private void senderLoop() {
communicator.send("LIST");
if (!executor.isShutdown()) {
@ -138,6 +184,11 @@ public class LobbyScene extends BaseScene {
}
}
/**
* Handles the selection of a multiplayer game channel.
*
* @param name The name of the selected channel.
*/
private void handleChannelSelected(String name) {
logger.info("Joining channel: " + name);
gameWindow.getMultiplayerGame().joinGame(name);

View File

@ -10,6 +10,7 @@ import javafx.scene.text.Text;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.App;
import uk.ac.soton.comp1206.effects.SoundPlayer;
import uk.ac.soton.comp1206.ui.GamePane;
import uk.ac.soton.comp1206.ui.GameWindow;
@ -90,10 +91,7 @@ public class MenuScene extends BaseScene {
leaderboardButton.setFocusTraversable(false);
menuBox.getChildren().add(leaderboardButton);
var settingsButton = new Button("Settings");
settingsButton.getStyleClass().add("menu-item");
settingsButton.setFocusTraversable(false);
menuBox.getChildren().add(settingsButton);
var quitButton = new Button("Quit");
quitButton.getStyleClass().add("menu-item");
@ -119,7 +117,8 @@ public class MenuScene extends BaseScene {
*/
@Override
public void initialise() {
SoundPlayer.stopBackgroundMusic();
SoundPlayer.playBackgroundMusic("/music/menu.mp3");
}
/**

View File

@ -1,14 +1,24 @@
package uk.ac.soton.comp1206.scene;
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.GameWindow;
public class MultiPlayerScene extends ChallengeScene {
private static final Logger logger = LogManager.getLogger(MultiPlayerScene.class);
private Communicator communicator;
public MultiPlayerScene(GameWindow gameWindow) {
super(gameWindow);
}
@Override
public void initialise() {
this.communicator = this.gameWindow.getCommunicator();
}

View File

@ -27,12 +27,11 @@ public class ScoresScene extends BaseScene {
Logger logger = LogManager.getLogger(ScoresScene.class);
private SimpleListProperty<Pair<String, Integer>> localScores = new SimpleListProperty<>(FXCollections.observableArrayList());
private SimpleListProperty<Pair<String, Integer>> onlineScores = new SimpleListProperty<>(FXCollections.observableArrayList());
private Leaderboard localLeaderboard = new Leaderboard(localScores);
private Leaderboard onlineLeaderboard = new Leaderboard(onlineScores);
private Boolean gameOver = false;
private SimpleListProperty<Pair<String, Integer>> localScores = new SimpleListProperty<>(FXCollections.observableArrayList());
private SimpleListProperty<Pair<String, Integer>> onlineScores = new SimpleListProperty<>(FXCollections.observableArrayList());
private Leaderboard localLeaderboard = new Leaderboard(localScores);
private Leaderboard onlineLeaderboard = new Leaderboard(onlineScores);
private Boolean gameOver = false;
/**
* Create a new scene, passing in the GameWindow the scene will be displayed in
@ -43,14 +42,29 @@ public class ScoresScene extends BaseScene {
super(gameWindow);
}
/**
* initialise
*/
@Override
public void initialise() {
loadLocalScores();
this.onlineScores = this.getOnlineScores();
this.scene.setOnKeyPressed(e -> this.gameWindow.startMenu());
}
/**
* load local scores
*/
private void loadLocalScores() {
ObservableList<Pair<String, Integer>> scores = this.gameWindow.getLocalStorage().getTopScores();
localScores.set(scores);
}
/**
* build layout
*/
@Override
public void build() {
logger.info("Building " + this.getClass().getName());
@ -103,19 +117,20 @@ public class ScoresScene extends BaseScene {
var onlineScoreHeading = new Text("Online Scores");
onlineScoreHeading.getStyleClass().add("heading");
onlineScoreBox.getChildren().add(onlineScoreHeading);
leaderboardBox.setAlignment(Pos.CENTER);
onlineScoreBox.getChildren().add(onlineLeaderboard);
onlineLeaderboard.setMinHeight(gameWindow.getHeight() / 4);
onlineLeaderboard.setAlignment(Pos.CENTER);
localLeaderboard.setAlignment(Pos.CENTER);
}
/**
* Fetches online scores from the server
* @return
* Fetch online scores
* @return online scores
*/
private SimpleListProperty<Pair<String, Integer>> getOnlineScores() {
Communicator communicator = gameWindow.getCommunicator();
@ -137,15 +152,18 @@ public class ScoresScene extends BaseScene {
e.printStackTrace();
return null;
}
}
/**
* Takes a string of high scores and converts it
* parse the string format of the online scores
* @param scoresString
* @return SimpleListProperty of high scores
* @param highScoresList
* @return
*/
private SimpleListProperty<Pair<String, Integer>> parseHighScores(String scoresString, SimpleListProperty<Pair<String, Integer>> highScoresList) {
String[] lines = scoresString.split("\n");
for (String line : lines) {
String[] parts = line.split(":");
@ -157,8 +175,13 @@ public class ScoresScene extends BaseScene {
}
return highScoresList;
}
/**
* setup as game over
* @param gameOver
*/
public void setGameOver(boolean gameOver) {
this.gameOver = gameOver;
}

View File

@ -1,69 +0,0 @@
package uk.ac.soton.comp1206.state;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.network.Communicator;
import uk.ac.soton.comp1206.scene.LobbyScene;
public class MultiplayerGame {
private StringProperty name;
private ObservableList<String> players;
private Communicator communicator;
Logger logger = LogManager.getLogger(MultiplayerGame.class);
public MultiplayerGame(Communicator communicator) {
this.communicator = communicator;
name = new SimpleStringProperty();
players = FXCollections.observableArrayList();
}
public void networkHandler(String response) {
try {
logger.info("Message received");
} catch (Exception e) {
logger.error("Failed to process multiplayer games: " + e.getMessage());
}
}
public String getName() {
return name.get();
}
public void joinGame(String name) {
this.communicator.send("JOIN " + name);
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public ObservableList<String> getPlayers() {
return players;
}
public void addPlayer(String player) {
players.add(player);
}
public void leave() {
communicator.send("PART");
name.set("");
}
public void sendMessage(String message) {
communicator.send("MSG " + message);
}
}

View File

@ -0,0 +1,106 @@
package uk.ac.soton.comp1206.tools;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.util.Pair;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* This class manages local storage of top scores, ensuring persistence and initialization of storage file.
*/
public class LocalStorage {
private SimpleListProperty<Pair<String, Integer>> topScores;
private static final String SCORES_FILE_NAME = "topScores.txt";
private Path scoresFilePath;
/**
* Constructor for LocalStorage initializes an empty list of top scores and sets up the file path.
*/
public LocalStorage() {
scoresFilePath = Paths.get(SCORES_FILE_NAME);
ObservableList<Pair<String, Integer>> observableList = FXCollections.observableArrayList();
this.topScores = new SimpleListProperty<>(observableList);
initFile();
loadScores();
}
/**
* Ensures the scores file is created if it does not exist.
*/
private void initFile() {
try {
if (Files.notExists(scoresFilePath)) {
Files.createFile(scoresFilePath);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Adds a new score to the list of top scores and saves the updated list to a file.
* @param playerName The name of the player
* @param score the score to add
*/
public void addScore(String playerName, int score) {
this.topScores.add(new Pair<>(playerName, score));
saveScores();
}
/**
* Saves the current list of top scores to a text file.
*/
public void saveScores() {
try (BufferedWriter writer = Files.newBufferedWriter(scoresFilePath)) {
for (Pair<String, Integer> score : topScores.get()) {
writer.write(score.getKey() + ":" + score.getValue());
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Loads top scores from a text file.
*/
private void loadScores() {
List<Pair<String, Integer>> scores = new ArrayList<>();
try (Scanner scanner = new Scanner(scoresFilePath.toFile())) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] parts = line.split(":");
if (parts.length == 2) {
String name = parts[0];
Integer score = Integer.parseInt(parts[1]);
scores.add(new Pair<>(name, score));
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException | NumberFormatException e) {
e.printStackTrace();
}
while (scores.size() < 10) {
scores.add(new Pair<>("", null));
}
this.topScores.set(FXCollections.observableArrayList(scores));
}
/**
* Returns the list of top scores.
* @return an observable list of top scores
*/
public ObservableList<Pair<String, Integer>> getTopScores() {
return this.topScores.get();
}
}

View File

@ -0,0 +1,149 @@
package uk.ac.soton.comp1206.tools;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.network.Communicator;
public class MultiplayerGame {
private StringProperty name;
private ObservableList<String> players;
private Communicator communicator;
private BooleanProperty isHost = new SimpleBooleanProperty(false);
private ObservableList<String> msg = FXCollections.observableArrayList();
Logger logger = LogManager.getLogger(MultiplayerGame.class);
public MultiplayerGame(Communicator communicator) {
this.communicator = communicator;
name = new SimpleStringProperty("");
players = FXCollections.observableArrayList();
isHost.set(false);
}
public void networkHandler(String response) {
try {
logger.info("Network response: " + response);
if (response.startsWith("USERS")) {
addPlayers(response.replace("USERS", "").trim());
} else if (response.startsWith("NICK")) {
handleNicknameChange(response.replace("NICK ", ""));
} else if (response.startsWith("HOST")) {
isHost.set(true);
} else if (response.startsWith("PARTED")) {
handlePart();
} else if (response.startsWith("ERROR")) {
logger.error("Error received: " + response.replace("ERROR ", ""));
} else if (response.startsWith("MSG")) {
handleMSG(response.replace("MSG ", "".trim()));
}
} catch (Exception e) {
logger.error("Failed to process multiplayer games: " + e.getMessage());
}
}
private void handleMSG(String msg) {
Platform.runLater(() -> {
logger.info("Recv chat message");
String[] parts = msg.split(":", 2);
getMessages().add("[" + parts[0] + "] " + parts[1]);
});
}
private void handleNicknameChange(String nickname) {
Platform.runLater(() -> {
});
logger.info("Nickname changed to: " + nickname);
}
private void handlePart() {
Platform.runLater(() -> {
name.set("");
players.clear();
isHost.set(false);
msg.clear();
});
logger.info("Left the channel");
}
private void addPlayers(String playerString) {
if (playerString.isEmpty()) {
logger.info("No players received in the string");
return;
}
String[] playerNames = playerString.split("\n");
Platform.runLater(() -> {
players.clear();
for (String name : playerNames) {
if (!name.trim().isEmpty()) {
players.add(name.trim());
}
}
logger.info("Players added: " + players);
});
}
// Getters and Setters
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public ObservableList<String> getPlayers() {
return players;
}
public BooleanProperty isHostProperty() {
return isHost;
}
public void joinGame(String name) {
communicator.send("JOIN " + name);
this.name.set(name);
}
public void createGame(String name){
communicator.send("CREATE " + name);
this.name.set(name);
isHost.set(true);
}
public void leave() {
communicator.send("PART");
handlePart();
}
public void sendMessage(String message) {
communicator.send("MSG " + message);
}
public void changeNickname(String newNickname) {
communicator.send("NICK " + newNickname);
}
public ObservableList getMessages() {
return msg;
}
public void startGame() {
if (isHost.get()) {
communicator.send("START");
} else {
logger.error("Attempt to start a game without host privileges");
}
}
}

View File

@ -9,9 +9,11 @@ import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.ac.soton.comp1206.App;
import uk.ac.soton.comp1206.effects.SoundPlayer;
import uk.ac.soton.comp1206.network.Communicator;
import uk.ac.soton.comp1206.scene.*;
import uk.ac.soton.comp1206.state.MultiplayerGame;
import uk.ac.soton.comp1206.tools.LocalStorage;
import uk.ac.soton.comp1206.tools.MultiplayerGame;
/**
* The GameWindow is the single window for the game where everything takes place. To move between screens in the game,
@ -33,7 +35,11 @@ public class GameWindow {
private Scene scene;
final Communicator communicator;
private MultiplayerGame multiplayerGame;
private LocalStorage localStorage;
/**
* Create a new GameWindow attached to the given stage with the specified width and height
* @param stage stage
@ -63,6 +69,12 @@ public class GameWindow {
//Set up the multiplayer handler
multiplayerGame = new MultiplayerGame(communicator);
//Setup local storage
localStorage = new LocalStorage();
}
/**
@ -188,4 +200,8 @@ public class GameWindow {
public MultiplayerGame getMultiplayerGame() {
return multiplayerGame;
}
public LocalStorage getLocalStorage() {
return localStorage;
}
}

View File

@ -68,6 +68,7 @@
-fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1);
}
.big-heading {
-fx-fill: white;
-fx-font-family: 'Orbitron';
@ -229,6 +230,15 @@ TextField {
-fx-background-color: none;
}
.small-button {
-fx-text-fill: white;
-fx-font-family: 'Orbitron';
-fx-font-size: 16px;
-fx-font-weight: 700;
-fx-effect: dropshadow(gaussian, black, 1, 1.0, 1, 1);
-fx-background-color: none;
}
.channel-item {
-fx-text-fill: white;
-fx-font-family: 'Orbitron';
@ -238,6 +248,11 @@ TextField {
-fx-background-color: none;
}
.message-box {
-fx-border-color: white;
-fx-background-color: transparent;
}
.multiplayer-game-list {
-fx-background-color: rgba(0, 0, 0, 0.7);
-fx-background-radius: 5px;
@ -250,4 +265,50 @@ TextField {
.title-image {
-fx-max-width: 100;
}
.messages-list, .players-list {
-fx-font-family: 'Orbitron';
-fx-background-color: rgba(0, 0, 0, 0.7);
-fx-background-insets: 5;
-fx-border-width: 1;
-fx-border-insets: 5;
-fx-padding: 10px;
}
.player-item {
-fx-background-color: transparent;
-fx-padding: 6px;
-fx-font-family: 'Orbitron';
-fx-font-size: 16px;
-fx-text-fill: Yellow;
}
.message-item {
-fx-background-color: transparent;;
-fx-font-family: 'Orbitron';
-fx-font-size: 12px;
-fx-text-fill: black;
}
.message-input {
-fx-background-color: transparent;
-fx-padding: 2px;
-fx-font-family: 'Orbitron';
}
.message-input {
-fx-background-color: rgba(255, 255, 255, 0.5);
-fx-text-fill: #FFFFFF;
}
.send-button {
-fx-background-color: #334499;
-fx-text-fill: #FFFFFF;
-fx-font-weight: bold;
-fx-cursor: hand;
}

View File