From 1332a42bf65645e4172342f06d8664923c7615ca Mon Sep 17 00:00:00 2001
From: lars <larslukasneuhaus@gmx.de>
Date: Mon, 21 Oct 2024 00:27:58 +0200
Subject: [PATCH] added tetromino controlls

---
 .../game/stateless/types/tetris/Tetris.java   |  18 ++-
 .../types/tetris/game/Playfield.java          |   4 +
 .../types/tetris/game/TetrisGame.java         |  52 +++++++-
 .../types/tetris/game/Tetromino.java          | 117 ++++++++++++++++--
 4 files changed, 174 insertions(+), 17 deletions(-)

diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java
index ff123f2..3626e30 100644
--- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java
@@ -46,7 +46,7 @@ class Tetris extends StatelessGame {
     protected void onStart() {
         super.onStart();
 
-        this.tetrisGame.generate();
+        this.tetrisGame.start();
     }
 
     protected void pressedButton(Button button) {
@@ -54,9 +54,18 @@ class Tetris extends StatelessGame {
         lastPresses.put(button, System.currentTimeMillis());
 
         switch (button) {
-            case A -> System.out.println("A");
-            case S -> System.out.println("S");
-            case D -> System.out.println("D");
+            case A -> {
+                System.out.println("A");
+                System.out.println(this.tetrisGame.currentTetromino.moveLeft());
+            }
+            case S -> {
+                System.out.println("S");
+                System.out.println(this.tetrisGame.currentTetromino.moveDown());
+            }
+            case D -> {
+                System.out.println("D");
+                System.out.println(this.tetrisGame.currentTetromino.moveRight());
+            }
             case W -> System.out.println("W");
             case mouseLeft -> System.out.println("mouse left");
             case mouseRight -> System.out.println("mouse right");
@@ -71,6 +80,7 @@ class Tetris extends StatelessGame {
     @Override
     protected void onLoad(@NotNull CompletableFuture<Void> callback) {
         this.tetrisGame = new TetrisGame(this, getSpawn().sub(6, 8, 15));
+        this.tetrisGame.generate();
     }
 
     @Override
diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java
index 2710d85..44bf742 100644
--- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java
@@ -17,6 +17,10 @@ public class Playfield {
         return lowerLeftCorner.add(6, 8, 15);
     }
 
+    public Pos getTetrominoSpawnPosition() {
+        return this.lowerLeftCorner.add(5, 21, 1);
+    }
+
     public void generate(StatelessGame instance) {
         AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
 
diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java
index 9e6401b..d3be554 100644
--- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java
@@ -1,18 +1,26 @@
 package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
 
 import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
+import net.minestom.server.MinecraftServer;
 import net.minestom.server.coordinate.Pos;
+import net.minestom.server.timer.Scheduler;
+import net.minestom.server.timer.TaskSchedule;
+
+import java.util.Random;
 
 public class TetrisGame {
     private final StatelessGame instance;
     private final Playfield playfield;
     private int speed = 1;
     private int lines = 0;
-    private Tetromino currentTetromino;
+    public Tetromino currentTetromino;
     private Tetromino nextTetromino;
+    private final Random random = new Random();
+
+    private final Pos tetrominoSpawnPosition;
 
     public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) {
-        this(instance, lowerLeftCorner, new Tetromino(Tetromino.Shape.J), new Tetromino(Tetromino.Shape.T));
+        this(instance, lowerLeftCorner, new Tetromino(instance, Tetromino.Shape.J), new Tetromino(instance, Tetromino.Shape.T));
     }
 
     public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) {
@@ -20,9 +28,49 @@ public class TetrisGame {
         this.currentTetromino = startTetromino;
         this.nextTetromino = nextTetromino;
         this.instance = instance;
+
+        this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition();
+    }
+
+    public void start() {
+        Scheduler scheduler = MinecraftServer.getSchedulerManager();
+        scheduler.submitTask(() -> {
+            this.tick();
+            return TaskSchedule.tick(40/this.speed);
+        });
     }
 
     public void generate() {
         this.playfield.generate(this.instance);
+
+        this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
+        this.currentTetromino.draw();
+    }
+
+    public void tick() {
+        if(!currentTetromino.moveDown()) {
+            currentTetromino = nextTetromino;
+            currentTetromino.setPosition(this.tetrominoSpawnPosition);
+            currentTetromino.draw();
+            nextTetromino = getNextTetromino();
+        }
+    }
+
+
+    private Tetromino getNextTetromino() {
+        int randomNumber = random.nextInt(1, 7);
+        Tetromino.Shape nextShape;
+
+        switch (randomNumber) {
+            case 2 -> nextShape = Tetromino.Shape.J;
+            case 3 -> nextShape = Tetromino.Shape.L;
+            case 4 -> nextShape = Tetromino.Shape.O;
+            case 5 -> nextShape = Tetromino.Shape.S;
+            case 6 -> nextShape = Tetromino.Shape.T;
+            case 7 -> nextShape = Tetromino.Shape.Z;
+            default -> nextShape = Tetromino.Shape.I;
+        }
+
+        return new Tetromino(this.instance, nextShape);
     }
 }
diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java
index 71bad89..00ddf1e 100644
--- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java
@@ -1,14 +1,16 @@
 package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
 
 import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
-import eu.mhsl.minenet.minigames.util.BatchUtil;
 import net.minestom.server.coordinate.Pos;
-import net.minestom.server.instance.batch.AbsoluteBlockBatch;
 import net.minestom.server.instance.block.Block;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class Tetromino {
     private final Shape shape;
-
+    private final StatelessGame instance;
+    private Pos position;
     private int[][] shapeArray;
 
     public enum Shape {
@@ -21,7 +23,8 @@ public class Tetromino {
         Z
     }
 
-    public Tetromino(Shape shape) {
+    public Tetromino(StatelessGame instance, Shape shape) {
+        this.instance = instance;
         this.shape = shape;
 
         switch (this.shape) {
@@ -35,6 +38,58 @@ public class Tetromino {
         }
     }
 
+    public Pos getPosition() {
+        return this.position;
+    }
+
+    public void setPosition(Pos newPosition) {
+        this.position = newPosition;
+    }
+
+    public boolean rotate(boolean clockwise) {
+        int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
+        return checkCollisionAndMove(this.position, newShapeArray);
+    }
+
+    public boolean moveDown() {
+        Pos newPosition = this.position.sub(0, 1, 0);
+        return checkCollisionAndMove(newPosition, this.shapeArray);
+    }
+
+    public boolean moveLeft() {
+        Pos newPosition = this.position.sub(1, 0, 0);
+        return checkCollisionAndMove(newPosition, this.shapeArray);
+    }
+
+    public boolean moveRight() {
+        Pos newPosition = this.position.add(1, 0, 0);
+        return checkCollisionAndMove(newPosition, this.shapeArray);
+    }
+
+    public void draw() {
+        getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock()));
+    }
+
+    public void remove() {
+        this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, Block.AIR));
+    }
+
+
+    private Block getColoredBlock() {
+        Block returnBlock;
+        switch (this.shape) {
+            case I -> returnBlock = Block.LIGHT_BLUE_CONCRETE;
+            case J -> returnBlock = Block.BLUE_CONCRETE;
+            case L -> returnBlock = Block.ORANGE_CONCRETE;
+            case O -> returnBlock = Block.YELLOW_CONCRETE;
+            case S -> returnBlock = Block.GREEN_CONCRETE;
+            case T -> returnBlock = Block.PURPLE_CONCRETE;
+            case Z -> returnBlock = Block.RED_CONCRETE;
+            default -> returnBlock = Block.WHITE_CONCRETE;
+        }
+        return returnBlock;
+    }
+
     private int[][] getTurnedShapeArray(boolean clockwise) {
         int iterations = 1;
         if(!clockwise) iterations = 3;
@@ -53,17 +108,57 @@ public class Tetromino {
         return returnArray;
     }
 
-    public void draw(StatelessGame instance, Pos spawnPosition) {
+    private boolean isPartOfTetromino(Pos position) {
+        for(Pos blockPosition : getBlockPositions()) {
+            if(position.sameBlock(blockPosition)) {
+                return true;
+            }
+        }
+        System.out.println("Not part of Tetromino!!!");
+        return false;
+    }
+
+    private List<Pos> getBlockPositions() {
+        List<Pos> returnList = new ArrayList<>();
+        if(this.position == null) return returnList;
+
         int arrayLength = this.shapeArray.length;
 
-        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
-
-        for(int x=-1; x<arrayLength-1; x++) {
-            for(int y=-1; y<arrayLength-1; y++) {
-                batch.setBlock(spawnPosition.add(x, y, 0), Block.WHITE_CONCRETE);
+        for(int x=0; x<arrayLength; x++) {
+            for(int y=0; y<arrayLength; y++) {
+                if(this.shapeArray[arrayLength-1-y][x] == 1) {
+                    returnList.add(this.position.add(x-1, y-1, 0));
+                }
             }
         }
 
-        BatchUtil.loadAndApplyBatch(batch, instance, () -> {});
+        return returnList;
+    }
+
+    private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
+        int arrayLength = newShapeArray.length;
+
+        for(int x=0; x<arrayLength; x++) {
+            for(int y=0; y<arrayLength; y++) {
+                Pos checkPosition = newPosition.add(x-1, y-1, 0);
+                if(isPartOfTetromino(checkPosition)) continue;
+                if(this.instance.getBlock(checkPosition) != Block.AIR) return true;
+            }
+        }
+
+        // TODO: collision check to -y not working
+
+        return false;
+    }
+
+    private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
+        if(!checkCollision(newPosition, newShapeArray)) {
+            this.remove();
+            this.shapeArray = newShapeArray;
+            this.setPosition(newPosition);
+            this.draw();
+            return true;
+        }
+        return false;
     }
 }