From 5acfe8f6af85b184e96c03e0052e8b56fd927657 Mon Sep 17 00:00:00 2001
From: lars <larslukasneuhaus@gmx.de>
Date: Sun, 20 Oct 2024 18:24:53 +0200
Subject: [PATCH] added basic tetris classes

---
 .../game/stateless/types/tetris/Tetris.java   | 43 +++---------
 .../stateless/types/tetris/TetrisFactory.java |  2 +-
 .../types/tetris/game/Playfield.java          | 37 ++++++++++
 .../types/tetris/game/TetrisGame.java         | 28 ++++++++
 .../types/tetris/game/Tetromino.java          | 69 +++++++++++++++++++
 5 files changed, 146 insertions(+), 33 deletions(-)
 create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java
 create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java
 create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java

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 421e5cb..ff123f2 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
@@ -1,8 +1,8 @@
 package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris;
 
 import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
+import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.TetrisGame;
 import eu.mhsl.minenet.minigames.score.FirstWinsScore;
-import eu.mhsl.minenet.minigames.util.BatchUtil;
 import eu.mhsl.minenet.minigames.instance.Dimension;
 import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
 import net.kyori.adventure.text.Component;
@@ -10,8 +10,6 @@ import net.minestom.server.coordinate.Pos;
 import net.minestom.server.coordinate.Vec;
 import net.minestom.server.entity.Player;
 import net.minestom.server.event.player.*;
-import net.minestom.server.instance.batch.AbsoluteBlockBatch;
-import net.minestom.server.instance.block.Block;
 import net.minestom.server.item.ItemStack;
 import net.minestom.server.item.Material;
 import org.jetbrains.annotations.NotNull;
@@ -21,9 +19,7 @@ import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
 class Tetris extends StatelessGame {
-    private final boolean isFast;
-    private final int width = 9;
-    private final int height = 20;
+    private TetrisGame tetrisGame;
 
     private final Map<Button, Long> lastPresses = new HashMap<>();
 
@@ -37,12 +33,10 @@ class Tetris extends StatelessGame {
         space
     }
 
-    public Tetris(boolean isFast) {
+    public Tetris() {
         super(Dimension.THE_END.key, "Tetris", new FirstWinsScore());
         this.setGenerator(new CircularPlateTerrainGenerator(30).setPlateHeight(0));
 
-        this.isFast = isFast;
-
         eventNode()
             .addListener(PlayerUseItemEvent.class, this::onPlayerInteract)
             .addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack);
@@ -51,6 +45,8 @@ class Tetris extends StatelessGame {
     @Override
     protected void onStart() {
         super.onStart();
+
+        this.tetrisGame.generate();
     }
 
     protected void pressedButton(Button button) {
@@ -74,25 +70,7 @@ class Tetris extends StatelessGame {
 
     @Override
     protected void onLoad(@NotNull CompletableFuture<Void> callback) {
-        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
-
-        for(int y = 45; y <= 45+height; y++) {
-            for(int x = -(width/2)-1; x <= (width/2)+1; x++) {
-                batch.setBlock(x, y, 0, Block.STONE);
-            }
-        }
-
-        for(int y = 45; y <= 45+height; y++) {
-            batch.setBlock(-(width/2)-1, y, 1, Block.GRAY_CONCRETE);
-            batch.setBlock((width/2)+1, y, 1, Block.GRAY_CONCRETE);
-        }
-        for(int x = -(width/2)-1; x <= (width/2)+1; x++) {
-            batch.setBlock(x, 45, 1, Block.GRAY_CONCRETE);
-        }
-
-        batch.setBlock((int) getSpawn().x(), (int) (getSpawn().y()-1), (int) getSpawn().z(), Block.STONE);
-
-        BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null));
+        this.tetrisGame = new TetrisGame(this, getSpawn().sub(6, 8, 15));
     }
 
     @Override
@@ -117,10 +95,10 @@ class Tetris extends StatelessGame {
         double forwardAmount = movementVector.dot(forward);
         double leftAmount = movementVector.dot(left);
 
-        if (forwardAmount > 0.01) {
+        if (forwardAmount > 0.018) {
             pressedButton(Button.W);
             releasedButton(Button.S);
-        } else if (forwardAmount < -0.01) {
+        } else if (forwardAmount < -0.018) {
             pressedButton(Button.S);
             releasedButton(Button.W);
         } else {
@@ -128,10 +106,10 @@ class Tetris extends StatelessGame {
             releasedButton(Button.S);
         }
 
-        if (leftAmount > 0.01) {
+        if (leftAmount > 0.018) {
             pressedButton(Button.D);
             releasedButton(Button.A);
-        } else if (leftAmount < -0.01) {
+        } else if (leftAmount < -0.018) {
             pressedButton(Button.A);
             releasedButton(Button.D);
         } else {
@@ -155,6 +133,7 @@ class Tetris extends StatelessGame {
     @Override
     protected boolean onPlayerJoin(Player p) {
         p.getInventory().setItemStack(0, ItemStack.builder(Material.BIRCH_BUTTON).customName(Component.text("Controller")).build());
+        p.setSprinting(false);
         return super.onPlayerJoin(p);
     }
 
diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java
index 02d6f3c..3b5b628 100644
--- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java
@@ -28,7 +28,7 @@ public class TetrisFactory implements GameFactory {
 
     @Override
     public Game manufacture(Room parent, Map<String, Option<?>> configuration) {
-        return new Tetris(false).setParent(parent);
+        return new Tetris().setParent(parent);
     }
 
     @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
new file mode 100644
index 0000000..2710d85
--- /dev/null
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java
@@ -0,0 +1,37 @@
+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;
+
+public class Playfield {
+    private final Pos lowerLeftCorner;
+
+    public Playfield(Pos lowerLeftCorner) {
+        this.lowerLeftCorner = lowerLeftCorner;
+    }
+
+    public Pos getPlayerSpawnPosition() {
+        return lowerLeftCorner.add(6, 8, 15);
+    }
+
+    public void generate(StatelessGame instance) {
+        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
+
+        for(int x=0; x<12; x++) {
+            for(int y=0; y<21; y++) {
+                batch.setBlock(lowerLeftCorner.add(x, y, 0), Block.STONE);
+
+                if(x==0 || x==11 || y==0) {
+                    batch.setBlock(lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE);
+                }
+            }
+        }
+
+        batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
+
+        BatchUtil.loadAndApplyBatch(batch, instance, () -> {});
+    }
+}
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
new file mode 100644
index 0000000..9e6401b
--- /dev/null
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java
@@ -0,0 +1,28 @@
+package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
+
+import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
+import net.minestom.server.coordinate.Pos;
+
+public class TetrisGame {
+    private final StatelessGame instance;
+    private final Playfield playfield;
+    private int speed = 1;
+    private int lines = 0;
+    private Tetromino currentTetromino;
+    private Tetromino nextTetromino;
+
+    public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) {
+        this(instance, lowerLeftCorner, new Tetromino(Tetromino.Shape.J), new Tetromino(Tetromino.Shape.T));
+    }
+
+    public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) {
+        this.playfield = new Playfield(lowerLeftCorner);
+        this.currentTetromino = startTetromino;
+        this.nextTetromino = nextTetromino;
+        this.instance = instance;
+    }
+
+    public void generate() {
+        this.playfield.generate(this.instance);
+    }
+}
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
new file mode 100644
index 0000000..71bad89
--- /dev/null
+++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java
@@ -0,0 +1,69 @@
+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;
+
+public class Tetromino {
+    private final Shape shape;
+
+    private int[][] shapeArray;
+
+    public enum Shape {
+        I,
+        J,
+        L,
+        O,
+        S,
+        T,
+        Z
+    }
+
+    public Tetromino(Shape shape) {
+        this.shape = shape;
+
+        switch (this.shape) {
+            case I -> shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
+            case J -> shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}};
+            case L -> shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}};
+            case O -> shapeArray = new int[][]{{1,1}, {1,1}};
+            case S -> shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}};
+            case T -> shapeArray = new int[][]{{0,1,0}, {1,1,1}, {0,0,0}};
+            case Z -> shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}};
+        }
+    }
+
+    private int[][] getTurnedShapeArray(boolean clockwise) {
+        int iterations = 1;
+        if(!clockwise) iterations = 3;
+
+        int arrayLength = this.shapeArray.length;
+        int[][] returnArray = new int[arrayLength][arrayLength];
+
+        for(int k=0; k<iterations; k++) {
+            for(int i=0; i<arrayLength; i++) {
+                for(int j=0; j<arrayLength; j++) {
+                    returnArray[i][arrayLength-1-j] = this.shapeArray[j][i];
+                }
+            }
+        }
+
+        return returnArray;
+    }
+
+    public void draw(StatelessGame instance, Pos spawnPosition) {
+        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);
+            }
+        }
+
+        BatchUtil.loadAndApplyBatch(batch, instance, () -> {});
+    }
+}