From f655435b6bd27940e903fc66b7eb7c0109ccfdb9 Mon Sep 17 00:00:00 2001 From: ayazb7 <60544937+ayazb7@users.noreply.github.com> Date: Thu, 7 Apr 2022 01:05:33 +0100 Subject: [PATCH] Adding Rotation and Swap mechanics --- .idea/comp1206-cw.iml | 9 + tetrecs/.idea/uiDesigner.xml | 124 +++++++ .../soton/comp1206/component/GameBlock.java | 341 +++++++++--------- .../soton/comp1206/component/GameBoard.java | 324 +++++++++-------- .../soton/comp1206/component/PieceBoard.java | 19 +- .../comp1206/event/NextPieceListener.java | 7 + .../comp1206/event/RightClickListener.java | 5 + .../java/uk/ac/soton/comp1206/game/Game.java | 37 +- .../java/uk/ac/soton/comp1206/game/Grid.java | 12 +- .../soton/comp1206/scene/ChallengeScene.java | 56 ++- .../soton/comp1206/component/GameBoard.class | Bin 4175 -> 4739 bytes .../uk/ac/soton/comp1206/game/Game.class | Bin 6194 -> 7125 bytes .../uk/ac/soton/comp1206/game/Grid.class | Bin 2919 -> 2663 bytes .../soton/comp1206/scene/ChallengeScene.class | Bin 7401 -> 8475 bytes tetrecs/tetrecs.iml | 34 ++ 15 files changed, 632 insertions(+), 336 deletions(-) create mode 100644 .idea/comp1206-cw.iml create mode 100644 tetrecs/.idea/uiDesigner.xml create mode 100644 tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java create mode 100644 tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java create mode 100644 tetrecs/tetrecs.iml diff --git a/.idea/comp1206-cw.iml b/.idea/comp1206-cw.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/comp1206-cw.iml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/tetrecs/.idea/uiDesigner.xml b/tetrecs/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/tetrecs/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project> \ No newline at end of file diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java index 272c525..50106ed 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBlock.java @@ -11,179 +11,186 @@ import org.apache.logging.log4j.Logger; /** * The Visual User Interface component representing a single block in the grid. - * + * <p> * Extends Canvas and is responsible for drawing itself. - * + * <p> * Displays an empty square (when the value is 0) or a coloured square depending on value. - * + * <p> * The GameBlock value should be bound to a corresponding block in the Grid model. */ public class GameBlock extends Canvas { - private static final Logger logger = LogManager.getLogger(GameBlock.class); - - /** - * The set of colours for different pieces - */ - public static final Color[] COLOURS = { - Color.TRANSPARENT, - Color.DEEPPINK, - Color.RED, - Color.ORANGE, - Color.YELLOW, - Color.YELLOWGREEN, - Color.LIME, - Color.GREEN, - Color.DARKGREEN, - Color.DARKTURQUOISE, - Color.DEEPSKYBLUE, - Color.AQUA, - Color.AQUAMARINE, - Color.BLUE, - Color.MEDIUMPURPLE, - Color.PURPLE - }; - - private final GameBoard gameBoard; - - private final double width; - private final double height; - - /** - * The column this block exists as in the grid - */ - private final int x; - - /** - * The row this block exists as in the grid - */ - private final int y; - - /** - * The value of this block (0 = empty, otherwise specifies the colour to render as) - */ - private final IntegerProperty value = new SimpleIntegerProperty(0); - - /** - * Create a new single Game Block - * @param gameBoard the board this block belongs to - * @param x the column the block exists in - * @param y the row the block exists in - * @param width the width of the canvas to render - * @param height the height of the canvas to render - */ - public GameBlock(GameBoard gameBoard, int x, int y, double width, double height) { - this.gameBoard = gameBoard; - this.width = width; - this.height = height; - this.x = x; - this.y = y; - - //A canvas needs a fixed width and height - setWidth(width); - setHeight(height); - - //Do an initial paint - paint(); - - //When the value property is updated, call the internal updateValue method - value.addListener(this::updateValue); - } - - /** - * When the value of this block is updated, - * @param observable what was updated - * @param oldValue the old value - * @param newValue the new value - */ - private void updateValue(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { - paint(); - } - - /** - * Handle painting of the block canvas - */ - public void paint() { - //If the block is empty, paint as empty - if(value.get() == 0) { - paintEmpty(); - } else { - //If the block is not empty, paint with the colour represented by the value - paintColor(COLOURS[value.get()]); - } - } - - /** - * Paint this canvas empty - */ - private void paintEmpty() { - var gc = getGraphicsContext2D(); - - //Clear - gc.clearRect(0.0D, 0.0D, this.width, this.height); - - //Fill - Color gradStart = Color.color(0.0D, 0.0D, 0.6D, 0.3D); - Color gradEnd = Color.color(0.0D, 0.0D, 0.2D, 0.5D); - gc.setFill(new LinearGradient(0.0D, 0.0D, 1.0D, 1.0D, true, CycleMethod.REFLECT, new Stop(0.0D, gradStart), new Stop(1.0D, gradEnd))); - gc.fillRect(0.0D, 0.0D, this.width, this.height); - - //Border - gc.setStroke(Color.WHITE); - gc.setGlobalAlpha(0.7); - gc.strokeRect(0,0,width,height); - } - - /** - * Paint this canvas with the given colour - * @param colour the colour to paint - */ - private void paintColor(Paint colour) { - var gc = getGraphicsContext2D(); - - //Clear - gc.clearRect(0,0,width,height); - - //Colour fill - gc.setFill(colour); - gc.setGlobalAlpha(1); - gc.fillRect(0,0, width, height); - - //Border - gc.setStroke(Color.BLACK); - gc.strokeRect(0,0,width,height); - } - - /** - * Get the column of this block - * @return column number - */ - public int getX() { - return x; - } - - /** - * Get the row of this block - * @return row number - */ - public int getY() { - return y; - } - - /** - * Get the current value held by this block, representing it's colour - * @return value - */ - public int getValue() { - return this.value.get(); - } - - /** - * Bind the value of this block to another property. Used to link the visual block to a corresponding block in the Grid. - * @param input property to bind the value to - */ - public void bind(ObservableValue<? extends Number> input) { - value.bind(input); + private static final Logger logger = LogManager.getLogger(GameBlock.class); + + /** + * The set of colours for different pieces + */ + public static final Color[] COLOURS = { + Color.TRANSPARENT, + Color.DEEPPINK, + Color.RED, + Color.ORANGE, + Color.YELLOW, + Color.YELLOWGREEN, + Color.LIME, + Color.GREEN, + Color.DARKGREEN, + Color.DARKTURQUOISE, + Color.DEEPSKYBLUE, + Color.AQUA, + Color.AQUAMARINE, + Color.BLUE, + Color.MEDIUMPURPLE, + Color.PURPLE + }; + + private final GameBoard gameBoard; + + private final double width; + private final double height; + + /** + * The column this block exists as in the grid + */ + private final int x; + + /** + * The row this block exists as in the grid + */ + private final int y; + + /** + * The value of this block (0 = empty, otherwise specifies the colour to render as) + */ + private final IntegerProperty value = new SimpleIntegerProperty(0); + + /** + * Create a new single Game Block + * + * @param gameBoard the board this block belongs to + * @param x the column the block exists in + * @param y the row the block exists in + * @param width the width of the canvas to render + * @param height the height of the canvas to render + */ + public GameBlock(GameBoard gameBoard, int x, int y, double width, double height) { + this.gameBoard = gameBoard; + this.width = width; + this.height = height; + this.x = x; + this.y = y; + + //A canvas needs a fixed width and height + setWidth(width); + setHeight(height); + + //Do an initial paint + paint(); + + //When the value property is updated, call the internal updateValue method + value.addListener(this::updateValue); + } + + /** + * When the value of this block is updated, + * + * @param observable what was updated + * @param oldValue the old value + * @param newValue the new value + */ + private void updateValue(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { + paint(); + } + + /** + * Handle painting of the block canvas + */ + public void paint() { + //If the block is empty, paint as empty + if (value.get() == 0) { + paintEmpty(); + } else { + //If the block is not empty, paint with the colour represented by the value + paintColor(COLOURS[value.get()]); } + } + + /** + * Paint this canvas empty + */ + private void paintEmpty() { + var gc = getGraphicsContext2D(); + + //Clear + gc.clearRect(0.0D, 0.0D, this.width, this.height); + + //Fill + Color gradStart = Color.color(0.0D, 0.0D, 0.6D, 0.3D); + Color gradEnd = Color.color(0.0D, 0.0D, 0.2D, 0.5D); + gc.setFill(new LinearGradient(0.0D, 0.0D, 1.0D, 1.0D, true, CycleMethod.REFLECT, new Stop(0.0D, gradStart), new Stop(1.0D, gradEnd))); + gc.fillRect(0.0D, 0.0D, this.width, this.height); + + //Border + gc.setStroke(Color.WHITE); + gc.setGlobalAlpha(0.7); + gc.strokeRect(0, 0, width, height); + } + + /** + * Paint this canvas with the given colour + * + * @param colour the colour to paint + */ + private void paintColor(Paint colour) { + var gc = getGraphicsContext2D(); + + //Clear + gc.clearRect(0, 0, width, height); + + //Colour fill + gc.setFill(colour); + gc.setGlobalAlpha(1); + gc.fillRect(0, 0, width, height); + + //Border + gc.setStroke(Color.BLACK); + gc.strokeRect(0, 0, width, height); + } + + /** + * Get the column of this block + * + * @return column number + */ + public int getX() { + return x; + } + + /** + * Get the row of this block + * + * @return row number + */ + public int getY() { + return y; + } + + /** + * Get the current value held by this block, representing it's colour + * + * @return value + */ + public int getValue() { + return this.value.get(); + } + + /** + * Bind the value of this block to another property. Used to link the visual block to a corresponding block in the Grid. + * + * @param input property to bind the value to + */ + public void bind(ObservableValue<? extends Number> input) { + value.bind(input); + } } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java index b587148..1131d32 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/GameBoard.java @@ -1,175 +1,199 @@ package uk.ac.soton.comp1206.component; +import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; 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.RightClickListener; import uk.ac.soton.comp1206.game.Grid; /** * A GameBoard is a visual component to represent the visual GameBoard. * It extends a GridPane to hold a grid of GameBlocks. - * + * <p> * The GameBoard can hold an internal grid of it's own, for example, for displaying an upcoming block. It also be * linked to an external grid, for the main game board. - * + * <p> * The GameBoard is only a visual representation and should not contain game logic or model logic in it, which should * take place in the Grid. */ public class GameBoard extends GridPane { - private static final Logger logger = LogManager.getLogger(GameBoard.class); - - /** - * Number of columns in the board - */ - private final int cols; - - /** - * Number of rows in the board - */ - private final int rows; - - /** - * The visual width of the board - has to be specified due to being a Canvas - */ - private final double width; - - /** - * The visual height of the board - has to be specified due to being a Canvas - */ - private final double height; - - /** - * The grid this GameBoard represents - */ - final Grid grid; - - /** - * The blocks inside the grid - */ - GameBlock[][] blocks; - - /** - * The listener to call when a specific block is clicked - */ - private BlockClickedListener blockClickedListener; - - - /** - * Create a new GameBoard, based off a given grid, with a visual width and height. - * @param grid linked grid - * @param width the visual width - * @param height the visual height - */ - public GameBoard(Grid grid, double width, double height) { - this.cols = grid.getCols(); - this.rows = grid.getRows(); - this.width = width; - this.height = height; - this.grid = grid; - - //Build the GameBoard - build(); + private static final Logger logger = LogManager.getLogger(GameBoard.class); + + /** + * Number of columns in the board + */ + private final int cols; + + /** + * Number of rows in the board + */ + private final int rows; + + /** + * The visual width of the board - has to be specified due to being a Canvas + */ + private final double width; + + /** + * The visual height of the board - has to be specified due to being a Canvas + */ + private final double height; + + /** + * The grid this GameBoard represents + */ + final Grid grid; + + /** + * The blocks inside the grid + */ + GameBlock[][] blocks; + + /** + * The listener to call when a specific block is clicked + */ + protected BlockClickedListener blockClickedListener; + + protected RightClickListener rightClickListener; + + /** + * Create a new GameBoard, based off a given grid, with a visual width and height. + * + * @param grid linked grid + * @param width the visual width + * @param height the visual height + */ + public GameBoard(Grid grid, double width, double height) { + this.cols = grid.getCols(); + this.rows = grid.getRows(); + this.width = width; + this.height = height; + this.grid = grid; + + //Build the GameBoard + build(); + } + + /** + * Create a new GameBoard with it's own internal grid, specifying the number of columns and rows, along with the + * visual width and height. + * + * @param cols number of columns for internal grid + * @param rows number of rows for internal grid + * @param width the visual width + * @param height the visual height + */ + public GameBoard(int cols, int rows, double width, double height) { + this.cols = cols; + this.rows = rows; + this.width = width; + this.height = height; + this.grid = new Grid(cols, rows); + + //Build the GameBoard + build(); + } + + /** + * Get a specific block from the GameBoard, specified by it's row and column + * + * @param x column + * @param y row + * @return game block at the given column and row + */ + public GameBlock getBlock(int x, int y) { + return blocks[x][y]; + } + + /** + * Build the GameBoard by creating a block at every x and y column and row + */ + protected void build() { + logger.info("Building grid: {} x {}", cols, rows); + + setMaxWidth(width); + setMaxHeight(height); + + setGridLinesVisible(true); + + blocks = new GameBlock[cols][rows]; + + for (var y = 0; y < rows; y++) { + for (var x = 0; x < cols; x++) { + createBlock(x, y); + } } - - /** - * Create a new GameBoard with it's own internal grid, specifying the number of columns and rows, along with the - * visual width and height. - * - * @param cols number of columns for internal grid - * @param rows number of rows for internal grid - * @param width the visual width - * @param height the visual height - */ - public GameBoard(int cols, int rows, double width, double height) { - this.cols = cols; - this.rows = rows; - this.width = width; - this.height = height; - this.grid = new Grid(cols,rows); - - //Build the GameBoard - build(); - } - - /** - * Get a specific block from the GameBoard, specified by it's row and column - * @param x column - * @param y row - * @return game block at the given column and row - */ - public GameBlock getBlock(int x, int y) { - return blocks[x][y]; - } - - /** - * Build the GameBoard by creating a block at every x and y column and row - */ - protected void build() { - logger.info("Building grid: {} x {}",cols,rows); - - setMaxWidth(width); - setMaxHeight(height); - - setGridLinesVisible(true); - - blocks = new GameBlock[cols][rows]; - - for(var y = 0; y < rows; y++) { - for (var x = 0; x < cols; x++) { - createBlock(x,y); - } - } - } - - /** - * Create a block at the given x and y position in the GameBoard - * @param x column - * @param y row - */ - protected GameBlock createBlock(int x, int y) { - var blockWidth = width / cols; - var blockHeight = height / rows; - - //Create a new GameBlock UI component - GameBlock block = new GameBlock(this, x, y, blockWidth, blockHeight); - - //Add to the GridPane - add(block,x,y); - - //Add to our block directory - blocks[x][y] = block; - - //Link the GameBlock component to the corresponding value in the Grid - block.bind(grid.getGridProperty(x,y)); - - //Add a mouse click handler to the block to trigger GameBoard blockClicked method - block.setOnMouseClicked((e) -> blockClicked(e, block)); - - return block; - } - - /** - * Set the listener to handle an event when a block is clicked - * @param listener listener to add - */ - public void setOnBlockClick(BlockClickedListener listener) { - this.blockClickedListener = listener; + } + + /** + * Create a block at the given x and y position in the GameBoard + * + * @param x column + * @param y row + */ + protected GameBlock createBlock(int x, int y) { + var blockWidth = width / cols; + var blockHeight = height / rows; + + //Create a new GameBlock UI component + GameBlock block = new GameBlock(this, x, y, blockWidth, blockHeight); + + //Add to the GridPane + add(block, x, y); + + //Add to our block directory + blocks[x][y] = block; + + //Link the GameBlock component to the corresponding value in the Grid + block.bind(grid.getGridProperty(x, y)); + + //Add a mouse click handler to the block to trigger GameBoard blockClicked method + //block.setOnMouseClicked((e) -> blockClicked(e, block)); + + block.setOnMouseClicked(e -> { + if (e.getButton() == MouseButton.PRIMARY) { + blockClicked(block); + } else { + blockRightClicked(block); + } + }); + return block; + } + + /** + * Set the listener to handle an event when a block is clicked + * + * @param listener listener to add + */ + public void setOnBlockClick(BlockClickedListener listener) { + this.blockClickedListener = listener; + } + + public void setOnRightClick(RightClickListener listener) { + this.rightClickListener = listener; + } + + /** + * Triggered when a block is clicked. Call the attached listener. + * + * @param block block clicked on + */ + private void blockClicked(GameBlock block) { + logger.info("Block clicked: {}", block); + + if (blockClickedListener != null) { + blockClickedListener.blockClicked(block); } + } - /** - * Triggered when a block is clicked. Call the attached listener. - * @param event mouse event - * @param block block clicked on - */ - private void blockClicked(MouseEvent event, GameBlock block) { - logger.info("Block clicked: {}", block); - - if(blockClickedListener != null) { - blockClickedListener.blockClicked(block); - } + private void blockRightClicked(GameBlock block) { + if (this.rightClickListener != null) { + this.rightClickListener.rightClicked(); } + } } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java index c2d35fd..5abd200 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/component/PieceBoard.java @@ -1,16 +1,29 @@ package uk.ac.soton.comp1206.component; +import uk.ac.soton.comp1206.event.BlockClickedListener; import uk.ac.soton.comp1206.game.GamePiece; public class PieceBoard extends GameBoard { - private GamePiece pieceToDisplay; - public PieceBoard(double width, double height) { super(3, 3, width, height); } + public void displayPiece(GamePiece piece, int placeX, int placeY) { + clear(); + this.grid.playPiece(piece, placeX + 1, placeY + 1); + } + public void displayPiece(GamePiece piece) { - this.pieceToDisplay = piece; + displayPiece(piece, 0, 0); + } + + public void clear() { + this.grid.clear(); + } + + public void setOnClick(BlockClickedListener handler) { + this.blockClickedListener = handler; } + } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java new file mode 100644 index 0000000..09971bd --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/NextPieceListener.java @@ -0,0 +1,7 @@ +package uk.ac.soton.comp1206.event; + +import uk.ac.soton.comp1206.game.GamePiece; + +public interface NextPieceListener { + void nextPiece(GamePiece paramGamePiece); +} diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java new file mode 100644 index 0000000..9b2c5ab --- /dev/null +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/event/RightClickListener.java @@ -0,0 +1,5 @@ +package uk.ac.soton.comp1206.event; + +public interface RightClickListener { + void rightClicked(); +} 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 af904f4..fb971dd 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 @@ -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.component.GameBlock; +import uk.ac.soton.comp1206.event.NextPieceListener; import uk.ac.soton.comp1206.utility.Multimedia; import java.util.ArrayList; @@ -43,6 +44,8 @@ public class Game { private int remainder = 0; + protected NextPieceListener nextPieceHandler = null; + /** * Create a new game with the specified rows and columns. Creates a corresponding grid model. * @param cols number of columns @@ -77,7 +80,6 @@ public class Game { this.nextPiece = spawnPiece(); nextPiece(); - this.currentPiece = spawnPiece(); } /** @@ -93,14 +95,19 @@ public class Game { logger.info("Block clicked at x = {} y = {}",x,y); boolean placed = grid.playPiece(currentPiece, x, y); if (placed) { + logger.info("Placing block at x:{} y:{}",x,y); + Multimedia.playSound("place.wav"); this.afterPiece(); this.nextPiece(); + } else { + logger.info("Cannot place piece here"); + Multimedia.playSound("fail.wav"); } } public GamePiece spawnPiece() { Random rand = new Random(); - GamePiece piece = GamePiece.createPiece(3, rand.nextInt(3)); + GamePiece piece = GamePiece.createPiece(rand.nextInt(15), rand.nextInt(3)); logger.info("Spawning piece: {}" , piece); return piece; } @@ -109,6 +116,10 @@ public class Game { this.currentPiece = this.nextPiece; this.nextPiece = spawnPiece(); + if (this.nextPieceHandler != null) { + this.nextPieceHandler.nextPiece(this.currentPiece); + } + logger.info("Current piece is : {}", this.currentPiece); logger.info("Next piece is: {}", this.nextPiece); @@ -195,6 +206,12 @@ public class Game { } } + public void swapPiece() { + GamePiece tempPiece = this.currentPiece; + this.currentPiece = this.nextPiece; + this.nextPiece = tempPiece; + } + /** * Get the grid model inside this game representing the game state of the board * @return game grid model @@ -219,6 +236,22 @@ public class Game { return rows; } + public GamePiece getCurrentPiece() { + return currentPiece; + } + + public GamePiece getNextPiece() { + return nextPiece; + } + + public void setOnNextPiece(NextPieceListener handler) { + this.nextPieceHandler = handler; + } + + public void rotatePiece(int rotations) { + this.currentPiece.rotate(rotations); + } + public IntegerProperty getLevel() { return level; } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java index 7905236..1e66ea3 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/game/Grid.java @@ -101,8 +101,6 @@ public class Grid { int value = blocks[x][y]; if (value == 0) continue; if (get(x + placeX, y + placeY) != 0) { - logger.info("Cannot place piece here"); - Multimedia.playSound("fail.wav"); return false; } } @@ -113,8 +111,6 @@ public class Grid { public boolean playPiece(GamePiece gamePiece, int placeX, int placeY) { if (!this.canPlayPiece(gamePiece, placeX, placeY)) return false; - logger.info("Placing block at x:{} y:{}",placeX,placeY); - Multimedia.playSound("place.wav"); int[][] blocks = gamePiece.getBlocks(); placeX = placeX - 1; placeY = placeY - 1; @@ -129,6 +125,14 @@ public class Grid { return true; } + public void clear() { + for (int y = 0; y < this.rows; y++) { + for (int x = 0; x < this.cols; x++) { + this.grid[x][y].set(0); + } + } + } + /** * Get the number of columns in this game * @return number of columns 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 b5fbf96..d2461ba 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 @@ -15,6 +15,7 @@ import uk.ac.soton.comp1206.component.GameBlock; import uk.ac.soton.comp1206.component.GameBoard; import uk.ac.soton.comp1206.component.PieceBoard; import uk.ac.soton.comp1206.game.Game; +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; @@ -29,6 +30,10 @@ public class ChallengeScene extends BaseScene { private static final Logger logger = LogManager.getLogger(MenuScene.class); protected Game game; + protected GameBoard board; + protected PieceBoard currentPieceBoard; + protected PieceBoard nextPieceBoard; + /** * Create a new Single Player challenge scene * @param gameWindow the Game Window @@ -50,7 +55,6 @@ public class ChallengeScene extends BaseScene { root = new GamePane(gameWindow.getWidth(),gameWindow.getHeight()); StackPane challengePane = new StackPane(); - challengePane.setBackground(new Background(new BackgroundFill[] { new BackgroundFill((Paint) Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY) })); challengePane.setMaxWidth(gameWindow.getWidth()); challengePane.setMaxHeight(gameWindow.getHeight()); challengePane.getStyleClass().add("menu-background"); @@ -114,11 +118,12 @@ public class ChallengeScene extends BaseScene { public HBox buildBoards() { HBox boards = new HBox(); - var board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2); + this.board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2); //Handle block on gameboard grid being clicked - board.setOnBlockClick(this::blockClicked); + this.board.setOnBlockClick(this::blockClicked); + this.board.setOnRightClick(this::rotateBlock); - boards.getChildren().add(board); + boards.getChildren().add(this.board); VBox gameInfo = new VBox(); @@ -143,11 +148,13 @@ public class ChallengeScene extends BaseScene { incomingTitle.getStyleClass().add("heading"); gameInfo.getChildren().add(incomingTitle); - PieceBoard currentPieceBoard = new PieceBoard(gameWindow.getWidth()/6, gameWindow.getWidth()/6); - gameInfo.getChildren().add(currentPieceBoard); + this.currentPieceBoard = new PieceBoard(gameWindow.getWidth()/6, gameWindow.getWidth()/6); + this.currentPieceBoard.setOnClick(this::rotateBlock); + gameInfo.getChildren().add(this.currentPieceBoard); - PieceBoard nextPieceBoard = new PieceBoard(gameWindow.getWidth()/10, gameWindow.getWidth()/10); - gameInfo.getChildren().add(nextPieceBoard); + this.nextPieceBoard = new PieceBoard(gameWindow.getWidth()/10, gameWindow.getWidth()/10); + this.nextPieceBoard.setOnClick(this::swapPiece); + gameInfo.getChildren().add(this.nextPieceBoard); gameInfo.setAlignment(Pos.CENTER); gameInfo.setSpacing(10); @@ -160,6 +167,13 @@ public class ChallengeScene extends BaseScene { return boards; } + private void swapPiece(GameBlock gameBlock) { + logger.info("Swapping current and next piece"); + this.currentPieceBoard.displayPiece(this.game.getNextPiece()); + this.nextPieceBoard.displayPiece(this.game.getCurrentPiece()); + this.game.swapPiece(); + } + /** * Handle when a block is clicked * @param gameBlock the Game Block that was clocked @@ -168,14 +182,27 @@ public class ChallengeScene extends BaseScene { game.blockClicked(gameBlock); } + private 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) { + rotateBlock(1); + } + + private void rotateBlock() { + rotateBlock(1); + } /** * Setup the game object and model */ public void setupGame() { logger.info("Starting a new challenge"); - Multimedia.playBackgroundMusic("game_start.wav", false); - Multimedia.getBackgroundPlayer().setOnEndOfMedia(() -> Multimedia.playBackgroundMusic("game.wav", true)); + //Start new game game = new Game(5, 5); } @@ -186,7 +213,16 @@ public class ChallengeScene extends BaseScene { @Override public void initialise() { logger.info("Initialising Challenge"); + Multimedia.playBackgroundMusic("game_start.wav", false); + Multimedia.getBackgroundPlayer().setOnEndOfMedia(() -> Multimedia.playBackgroundMusic("game.wav", true)); + + this.game.setOnNextPiece(this::nextPiece); game.start(); } + protected void nextPiece(GamePiece nextPiece) { + logger.info("Next piece to place: " + nextPiece); + this.currentPieceBoard.displayPiece(nextPiece); + this.nextPieceBoard.displayPiece(this.game.getNextPiece()); + } } diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/component/GameBoard.class b/tetrecs/target/classes/uk/ac/soton/comp1206/component/GameBoard.class index 5f8a073ae3bae068119a84b6ff70d71bff47c4bc..7d7f7ec15155e1956eee50ce3814870034b662b0 100644 GIT binary patch delta 1538 zcmX@F(5%XJ>ff$?3=9mm497Qe^|LB*GVn3v@i62w6fiOf6=kMplsM;PCTII(7MG;v zr4}(V=ue)=E;hN2l}#)NL)Myup=k0PRte4$9tJyxQbq>#$#qPs{F-6h4CM?J><pDW z3{?!(lRvT+3UD*jGSsm%)blVjFt9N)=uSSsA__9q5Ytpf1|Eb%Qd77Xni!hd8CrN4 zS{d3H8Du9HvdIcEGH|A+mN=D`l;r0zGAL<iPF~0*DGuRVb24-=bh0ya@i25V^e{5W zO)g}U5QfMxGOz~(dHOmAMKUrdfb>{F#I3m)_!ufcL_ZJ11cqux2EnA9{N!we>p%{f z#4wqiVG0k!REBAc49b%Q*=06AXIsuF!e5+P;-805%gA7)0Sh)!gfJ{ntu@0YZ{x^e zl%KqiOMbEfr#;^mE{3fP+t?Yl^DyjS*tt2ElZ8=dHzR|%hEG;vSz=m+erj22UWvXd zh_u!W<7Z{q3v%l|9){HnYXle$OrFbS#&VdEL3Q#yPVve5Tx{IMDn9x7*`)>S3`Zyb z=Q3vGoNUZ(Db2!=&5*;uz`z2Ea0UhjZU#mMR)*;ej0`g-*K=E$&tza=P-5U<U|?`% zU}11$;9+oQ5Ml6OkYw;=kZ15>P+{<8&|vUmn8m=rpvAz#z`&r%Fq>h{<PY4ctaHJp zNb%SpnG(ss!Vty4$q>!J#}LCH%n-{U$q>h&#*oOM#gGIx#e{*Cfq_9AY)UVWF8e$N zf3P_Rd8DjCu3=+fWME*(2CG$IU}j)o-~_2<00B;h`3yo}c}|7}3_=V{3@i+43?Lmd zCX4ZwMT7L~F>o_5FcdLJFqAMTFqATAFqAXsGE_3yGE_6TGt@8yGSo6eFw`)_F;p|8 zFf_pJ@njGMo6f{w&!7r69psKwsND-EKj)QYoH3b|FTEaQ3na`s7`Pca8N?ZS7}Oa0 z8T1$?F<3B6WpHDd#SqM}5Nxs?0~-SaLm1Q)5DVsmFt8m=4BQOf42u|K7?>Hv8SEJr zGsrNoGN>^aFf3tM%D~RR$gqrIIVgM>7#Qprm>8HC7$(2w;|05c3&dks$-v663T&4c zn9l~ci;ZCgLk<HY10%yqhE=F$FtBN9Z)M2c$iTqB!~pUS#H_UptPJaLnpMES$iM{3 z-wcfP3^N!Q7}6M+!B)y{V$c&R)ZWQZ%*>#*g(348122fu-pNqL!~n8`8El6TIQ?v8 z;AYs)AjGhPL55)$l3lWJcguqP&d9Kkfq{XUfl-Em36w#h9tZ<FqMm_4P-_cA?lA@) z5Vez`h7rjkK5(+x%fQaCk3oRp0Fotwa7zSHEfEl40_7~IB{>XCU?U`SwlOqrVrXB+ zz^$ddjiGlr0|$ui+gJ~F8Yti(o|?_T#W06Kh+!UsIKu+4eUPLp0k=;AY99lG1jAZ} zbqq{k@3Am239&G6Fsz3pVg?2VMR4+FVBi$u+rcn#GlL!|{20OE#|D;?U|?q0paG7! ojSQO@7#MgM7#TJ*Ff;68*u$`&;UEJ80~Z4$!y$$v496HG0Z2qAd;kCd delta 1055 zcmZoxJ+HuZ>ff$?3=9mm3>!9b^|LZ_Fyu{s%PPTEz{6n2P&k>JO^2_Tp@f~Gl!u{= zp`4LHfARqq(aCkJQj<&A?D@DD_!ufdGF6NWJ{pq~xa22q<WLpQ$xqI<)(qofs9~sO zXQ<<0sAp(kWKf=L$RV?tontv88*6G=YF^3YST2>xUpccTH*(qX&E;a4$1tCrVF3@r zLWV_~&v3CYGA@~Xkz1B;8OWC9JPcDArU@{tocxE|jAacYgX-i&9yu-*pZxsn(gJpd zb(7<Gj2Ss5PvWr@W@5-@$YEe$;ACKAU}b1zU}R{T{EWxSyqST4L5YEbfq}t^frY`D zfrr6`L4?7TL6X6ZL7u^#L50DKL4(1Yp@o5gL5qQffq_Ajp_QR+ayYLlYdhGKMqV2v zQ^FWn7{VDi86p_?7$O;j8KM{@8KN1~7~&YT7~;XEm@u$1FfeF?P5HvB8`Hty4>E^= zfx(@DiGdm9My)LjTGB=`MjIFy7?>D9PGn<XWME*(02`nHQ3Nu80R%W1IvIq(@|+A^ z3_=V{3@i+43?Lm%lWY0PqCxug7`Pc281fk;7z!B_7>XD)7)ls)8Oj)J87dgu87dh9 z8LAi}7%Ca!7%CW27;546cru8BO=n`TXHW&34su5-)b8%de*ChGO_Ou@)9XRDKmxLr zft#U?L7btJL5-n@L64!I!Gd8DgB!zChG2&2V3X|_*cccX!l0&rSTG-if$d;o;AZe< z=wXmyU}g|!uxIFHkYQkDP-8G)=ws+-U}s=tn7}X*8fbP5Obkp64D}3bTH0F~GB?75 zj0+^pFrR^yVFB1uF)*JEZYdkXB!(OYMg}H^GKMOsVFe7#;J}sL#GogXtFx0KpP4~x z3q$5H23`=Qvy-8Si2-B<GdNa+zzJdn12@B}dIlke)eJHWYr%FwqDU5Qmn_3%hAH4E zk_CH#iJ_Z;fq|KUQAU6Xl!l?!yF#sJ*V576#!vyW2s!$vBALw&H=7-7Vj3u3*cqlX zOkrSR;9!^miGKzL21T&73=Et?d^;GbH#6vgOl1T+pA9S}#=y)l6C7%@7-lmtFz_%i gGR$FMW?0Oylwk$KDh37yE(S)1)eLJH)-y-~0Eeil=l}o! 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 54142e5420e92cd5cec8ffadb11a9b80935bf8b5..9bee46cc02c85f4d236ae529187f1b0a6ca79b2f 100644 GIT binary patch literal 7125 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 bN-%;*DKII+2<rQAF)%X9Gb%DFGe`meRPCg> delta 3721 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 diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/game/Grid.class b/tetrecs/target/classes/uk/ac/soton/comp1206/game/Grid.class index 002e059f2affcc6dc3522671f50858fd1a9f5865..915e5a5f1411e7a79631ab290a29233d57d65412 100644 GIT binary patch delta 1309 zcmaDZ_FRPP)W2Q(7#J9A8KO6Gtz+ckV&GuV<zdic(4V}O$wmysHRNHCVK8E3VAJsQ z^wbPvXE2$3fLWH$jKQ3p!GedulEI3RL3y$vi_Bzp7B4YQ1`Y-r9tK+mJ4Ob!oc#3k z)FMU(#mxpRnT(=Aj0~IwIf<14nW@RCj0~*FIjM<7lb5l>SoPO@7BJ&CSKY$iT{A z&%nswFj<5xoY8S|4x7HR69WT-2?HyG5`!#*GXoC;3xgDcHiHWT4+Ap;1A`lbD}&qQ zeQe^4?vt;vDX@Dmc!Kr+Ws|CRWME)mV_;-pU~psrnW6yJ!3omA00Nv~^FZ>PVDp$5 z7(t!|>1SYIh-6@5U}0cj;MLl~AS7)hV<fwcL24rd0|OHS$Y3r8CI$uuZw4L)AF$zC z5XErAc^Q;Jt_7LS3pJgAftSGx>~2N|c?NBeJ(G8`Yk=L!2X<RH0}n$41201q+(=#q zCI)W?AFwPx+;D!l%lV;ZyF>iy!_J_{zzB*#km(Ezj3Nvy3>*v$3=!Jf7?iD8WLQ#c zWm!_}n9W#4StVKaEn;wB_A+A=Ws_tPWi{Hjh(QO;5M|RdV;5zg#lW_lK}1WGRVG1{ zO*Ua0gXT;IR^~M=Y>dnQpKoDhT>gI_%Nhm-24--G7%}in-pQd_@5PY95WtYk5XO)L z_C^o`2ZJGlB-rch3^EKRU?;ON2r{^Wy}=4{9^7jY489D0;7E&L@MquwhjIi%065s# z7=joA8G;yC7(m{YVqp8rAjr-T%=m|ai}4SG4C5~bfnN-2tiKpo85kKt81xw!85|fG z80Da0kf^0CBfE`3XBNm4lNWKi)f<4latZ8}gQ#9%U|=X<;9)3Y5Mn515MwA|kYXrf zP-Q4*FlMM=uw|%ZaAl}s2xcf}h-4^Zh-avUd)1af4C+-421BS<Wf`2{UQL91HL)J< z*F?Bq6QO<;V(>@ut26^U&tC=sRL}DMVu1QJ6r9c-7#J9)GBAPDiY~K_wAK~|-bD<m z%(gOGTNv0EF^GavvW$_m5%V?%sTL;2<@NtxwlFd-|9=ydtRayo#lXhEz|hFR#n8kc z!qCj1z|h2?#?Z*1!_WryAt(>=Fi3%Y!^FVEpau3JD3$9%{RN5|0S1;o3`~rF7&sY! zF-St<BaA@-5+C4r0qJ0!e2R+;ocbZTv=?jx4><B!p{6l1gk$Ohc?^{9AUP4Dc`6po r91IcgfB|I-aHbbB-oaqHnZX+5XGR7`5lF^iU|<kqU}T5{=kF*0E3vMX delta 1491 zcmaDZ@?4DT)W2Q(7#J9A8Ok<ttz+ckWZ+=X<zdic(4V}XNmrMH!H|(b+&M8XFTX^g zASW?7RiPj=H91uwBef`%o57gDgq^{Zhrx`&oRL9(vOSZGLwafvBLhojURpjQgQ$j2 zR$^JAeokUux_)p;QD$DcwPqLxgC!#aM_OWLj$V0U85e^!gAF@_Ef0eo$Po3(`HZ3( zr6rj;nI)C_zNI-OnYpPcnTd=HoCP_FmBIO?c_~~B91IRT42}#=lMgZ4)N?SnFfvF5 z<Rm6%=A|nn<>V)4D<qaER9IElDpXok*K#qqF}Slcc<?ZIGI%jE2qK*1nOBkub}xHb zVoqtQe;Ol$n1-h&lB1z2tU+G&0r}IHk--wxSHWO!A?fi?%1TWxvBneudEFo4k>pf} z-#Hipc^G6Ef*2XtG(0^$HN)5$LKqn&K!G6OSWv*l5XKPB&Je-F5XlfV*@h`|^BpD+ zM&3M#A(a83sF-ZY;>rw)uSOPWZcYY923Ce>21bUM$;(;78Dl5^XVF)VV_;w~VPIuY zVvuEsXW(IAVUS|bW=LS*VPIxpU~pqdWJsDE$STg5JUNq9fjxyG6|8>(t5kg~0|Nsa z10w?igChgT6a}yjPLK`;5a0xx2a@Lmo5#e!2=Wd{KLZ0pBm)xz3j+fKuhtd@A!#ES zBiU^XQX3f<7?>D926HhmF)%QAGw?9@fDPAzD25x(%b*N$Ey#3UsObz0ybNhzcQZ1` zGiZbCne4-+0d^-J*lpnqJPZ*GybMurBY7E^7}6Osz_R>s!};MZ=ZBh|4DqWEJA)zv zBPex&OlM$V)Mj8|;9y{2NY~!Rplrn=!;)ev%aUTpY{n|eD#@~M5raQ-q#2tin<R@U ztI@ti4Ax+VD4U)cyD0lC2Dar4%37kVG6|w=vI*N5G-oo1Y+}$6GT6yr#Ka(Ev4g>i zc})u&<MRIpT38vE|6k9thJk^B85}@X41AMq*;VU<7%~~c8L}868B!SH8Il=N8S=os z3u544Fl5jGhX^}^41+V+Cu|IY3?X3Ov4UI;_i;KyCPNlDF4GyZ8F;{fpU#j24ox<O zM21|3JO&m9P$)<-u>EBaWM{}{{KLS-_{Wt&hVd7J{4WO9Ukt1aj0^<~PSBvSg$7N9 zmbQ%SHU|4y46Mv+K)!R9*}>o{yMw`XCxbT&gOJ}22CK>J9B%aiV4q$B`}82HPZ<~( ziWqnpN*LrAN*R<H$`~{m${BPSDi{nIsu*k-su{c(Y8XNoY8he}>KL*Zsu>Cxsu;=` z8sQ-k!XO3>33~=ZXh@haIKe}rf+5kGAqkXf87kmGQ2`H%3TRMhGWa6}g){>@&tC?0 zc82`P|2ah23mFtZUZ1>=Qw*HuAbFz`>;@ii*s?;yn~|XiqLzVyL7#yUBB!;5ffp3L zkn|7HJQ0g#4u)cIwu@n404GL}qdA3wcQAx*X3zm;AV!8*5lC`nU|<kqU}Pu(=c-Zw DTdo7L 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 db3f11b372bf64a21aa8eddfd74d0f4317da0970..307d07de2a84933ea7a5dc80b9504e5c98c000a8 100644 GIT binary patch delta 4581 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 AaR2}S delta 3702 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 diff --git a/tetrecs/tetrecs.iml b/tetrecs/tetrecs.iml new file mode 100644 index 0000000..34834c2 --- /dev/null +++ b/tetrecs/tetrecs.iml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> + <component name="CheckStyle-IDEA-Module"> + <option name="configuration"> + <map /> + </option> + </component> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_17"> + <output url="file://$MODULE_DIR$/target/classes" /> + <output-test url="file://$MODULE_DIR$/target/test-classes" /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> + <excludeFolder url="file://$MODULE_DIR$/${project.build.directory}/classes" /> + <excludeFolder url="file://$MODULE_DIR$/${project.build.directory}/test-classes" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:win:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:win:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-base:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-base:win:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-fxml:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-fxml:win:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-media:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: org.openjfx:javafx-media:win:17.0.2" level="project" /> + <orderEntry type="library" name="Maven: com.neovisionaries:nv-websocket-client:2.14" level="project" /> + <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.1" level="project" /> + <orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-core:2.17.1" level="project" /> + </component> +</module> \ No newline at end of file -- GitLab