From 63228cf77e395af74d93c9c1b7fb3f16b27d6e1b Mon Sep 17 00:00:00 2001 From: ayazb7 <60544937+ayazb7@users.noreply.github.com> Date: Sat, 2 Apr 2022 00:54:40 +0100 Subject: [PATCH] Adding UI Changes --- .../java/uk/ac/soton/comp1206/game/Game.java | 44 ++++++++++++++++- .../soton/comp1206/scene/ChallengeScene.java | 46 ++++++++++++++++++ .../uk/ac/soton/comp1206/scene/MenuScene.java | 3 +- .../uk/ac/soton/comp1206/game/Game.class | Bin 4229 -> 5129 bytes .../ac/soton/comp1206/scene/MenuScene.class | Bin 3670 -> 3681 bytes 5 files changed, 89 insertions(+), 4 deletions(-) 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 21b1fb6..62c8b79 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 @@ -1,5 +1,6 @@ package uk.ac.soton.comp1206.game; +import javafx.beans.property.SimpleIntegerProperty; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.component.GameBlock; @@ -32,6 +33,14 @@ public class Game { private GamePiece currentPiece; + private SimpleIntegerProperty score; + + private SimpleIntegerProperty level; + + private SimpleIntegerProperty lives; + + private SimpleIntegerProperty multiplier; + /** * Create a new game with the specified rows and columns. Creates a corresponding grid model. * @param cols number of columns @@ -41,6 +50,12 @@ public class Game { this.cols = cols; this.rows = rows; + this.score = new SimpleIntegerProperty(0); + this.level = new SimpleIntegerProperty(0); + this.lives = new SimpleIntegerProperty(3); + this.multiplier = new SimpleIntegerProperty(1); + + //Create a new grid model to represent the game state this.grid = new Grid(cols,rows); } @@ -128,12 +143,17 @@ public class Game { logger.info("Removing rows/columns"); } + int numGridBlocks = 0; + /** * Removes all columns which are full */ for (int x : fullCols) { for (int y = 0; y < grid.getCols(); y++) { - grid.set(x,y,0); + if (grid.get(x,y) != 0) { + grid.set(x,y,0); + numGridBlocks++; + } } } @@ -142,9 +162,15 @@ public class Game { */ for (int y : fullRows) { for (int x = 0; x < grid.getCols(); x++) { - grid.set(x,y,0); + if (grid.get(x,y) != 0) { + grid.set(x,y,0); + numGridBlocks++; + } } } + + score = new SimpleIntegerProperty(score.get() + (fullCols.size() + fullRows.size()) * numGridBlocks * 10 * multiplier.get()); + System.out.println(score.get()); } /** @@ -171,5 +197,19 @@ public class Game { return rows; } + public SimpleIntegerProperty getLevel() { + return level; + } + + public SimpleIntegerProperty getLives() { + return lives; + } + + public SimpleIntegerProperty getScore() { + return score; + } + public SimpleIntegerProperty getMultiplier() { + return multiplier; + } } 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 fd6c21b..3e3951f 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 @@ -1,6 +1,9 @@ package uk.ac.soton.comp1206.scene; +import javafx.beans.property.Property; +import javafx.geometry.Pos; import javafx.scene.layout.*; +import javafx.scene.text.Text; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import uk.ac.soton.comp1206.component.GameBlock; @@ -49,6 +52,49 @@ public class ChallengeScene extends BaseScene { var board = new GameBoard(game.getGrid(),gameWindow.getWidth()/2,gameWindow.getWidth()/2); mainPane.setCenter(board); + HBox header = new HBox(); + + VBox scoreBox = new VBox(); + var scoreTitle = new Text("Score"); + scoreTitle.getStyleClass().add("heading"); + var scoreValue = new Text(); + scoreValue.textProperty().bind(game.getScore().asString()); + scoreValue.getStyleClass().add("score"); + scoreBox.getChildren().addAll(scoreTitle,scoreValue); + scoreBox.setAlignment(Pos.BASELINE_CENTER); + + header.getChildren().add(scoreBox); + + Region space = new Region(); + space.setPrefHeight(100); + space.setPrefWidth(100); + HBox.setHgrow(space,Priority.ALWAYS); + header.getChildren().add(space); + + var challengeTitle = new Text("Challenge Scene"); + challengeTitle.getStyleClass().add("title"); + header.getChildren().add(challengeTitle); + + Region space2 = new Region(); + space2.setPrefHeight(100); + space2.setPrefWidth(100); + HBox.setHgrow(space2,Priority.ALWAYS); + header.getChildren().add(space2); + + VBox livesBox = new VBox(); + var livesTitle = new Text("Lives"); + livesTitle.getStyleClass().add("heading"); + var livesValue = new Text(); + livesValue.textProperty().bind(game.getLives().asString()); + livesValue.getStyleClass().add("lives"); + livesBox.getChildren().addAll(livesTitle,livesValue); + livesBox.setAlignment(Pos.BASELINE_CENTER); + + header.getChildren().add(livesBox); + header.setAlignment(Pos.CENTER); + + mainPane.setTop(header); + //Handle block on gameboard grid being clicked board.setOnBlockClick(this::blockClicked); } diff --git a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java index 433d719..79c3ae1 100644 --- a/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java +++ b/tetrecs/src/main/java/uk/ac/soton/comp1206/scene/MenuScene.java @@ -48,7 +48,7 @@ public class MenuScene extends BaseScene { //Awful title var title = new Text("TetrECS"); - title.getStyleClass().add("title"); + title.getStyleClass().add("bigtitle"); menuWidgets.getChildren().add(title); VBox menuButtons = new VBox(); @@ -58,7 +58,6 @@ public class MenuScene extends BaseScene { singlePlay.setOnMouseClicked(this::startGame); menuButtons.getChildren().add(singlePlay); - menuButtons.setAlignment(Pos.CENTER); menuWidgets.setAlignment(Pos.TOP_CENTER); 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 be32772624ed05d9eb70fad41f3f0732615e7bea..cecf78d96f50f7fea2ff6433c5031c891306b191 100644 GIT binary patch literal 5129 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 HBL+zT)Zxy4 delta 2431 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 diff --git a/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class b/tetrecs/target/classes/uk/ac/soton/comp1206/scene/MenuScene.class index b58df00055fca0f690452354818862658dbb1c86..afc0c298de62f7b0ad2e34a37ff7f6c67d34139b 100644 GIT binary patch delta 329 zcmca6^H7HC)W2Q(7#J9A8IEk^+R4Jgk(8Of`3Q?JGaGA3W=T%!<g4s=`Brf;tY%ol z&ajq;L78FQ<O3W><To%fa5<G`=A>lir7JM<voUPqV%W^Eg`Ht555qQw?VA^KvM@64 zoV<cdR(UrU0|&z%9)^Vsiv$=JF*5LY=H;apIp-u67pE4pGaO)KP@QbZB|iB-mjL6T z%>vy283k<_o-^1nykl@+_{`wQ@RPxLvI4KE>>P%<3@i-m8N3<hG0bLQW!S~wz%ZX- zE(03_Bg2BpMZ8Lq-VBo%7#MgM7#SEC7#SurOkrSPU;<e@c`dKHY7hfELoir{7y}an z0|N(C1&FnnVF?2x0|&#>$v=7Z^p}D4EoWH4z`!8Hz{IeUfstW7!$t;Xh8+yM81^!N RLX3-nkzqf>L59N&k^u3bQUd@0 delta 331 zcmaDTb4`Zp)W2Q(7#J9A84hpc+R4Jmy7@SZF!SU`?6>(=axtu8Sk2C`hKE6!VeRD8 z97p8WGcs^Fm1gFoWagzSF!Hl8Y~*6t#ITv2VG9q#R)%ex*Kx8i+U;Ou5ZCa@N-RrE ztI$s^OU*0McLkBwnqmB`47<1(I2d;GFf3qLD8R6gk%7lEFE6#oIVZ8WIJKCaVgKY0 zTv{v#85!gz8*qwGF5qI@Y|VY2QP7UzIfFgJI|fIF&kRlsKN(ymYxA1QrZUW7U}0F# z;LR|XVKxIR!!8B~hItHg7}yvX8Rk!}<yB(znY@5klz9Qe!pS>$)m4KT*cn0?CNVHD zh%qoRFfed1OlFwE00JBgix?I&Ffwp3ESb#0r>DP^fq{XEVHv}61_lNp1}26T42%rx f7&b64Gi+zr$*_lEF9QPu7Xu^1K86DfhZrOQ4SQ8W -- GitLab