fixed collision detection, added rotation

This commit is contained in:
Lars Neuhaus 2024-10-21 15:55:54 +02:00
parent 1332a42bf6
commit 236d372746
4 changed files with 139 additions and 38 deletions

View File

@ -50,25 +50,31 @@ class Tetris extends StatelessGame {
} }
protected void pressedButton(Button button) { protected void pressedButton(Button button) {
if(lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis()-200) return; if(lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis()-100) return;
lastPresses.put(button, System.currentTimeMillis()); lastPresses.put(button, System.currentTimeMillis());
switch (button) { switch (button) {
case A -> { case A -> {
System.out.println("A"); System.out.println("A");
System.out.println(this.tetrisGame.currentTetromino.moveLeft()); System.out.println(this.tetrisGame.moveLeft());
} }
case S -> { case S -> {
System.out.println("S"); System.out.println("S");
System.out.println(this.tetrisGame.currentTetromino.moveDown()); System.out.println(this.tetrisGame.moveDown());
} }
case D -> { case D -> {
System.out.println("D"); System.out.println("D");
System.out.println(this.tetrisGame.currentTetromino.moveRight()); System.out.println(this.tetrisGame.moveRight());
} }
case W -> System.out.println("W"); case W -> System.out.println("W");
case mouseLeft -> System.out.println("mouse left"); case mouseLeft -> {
case mouseRight -> System.out.println("mouse right"); System.out.println("mouse left");
System.out.println(this.tetrisGame.rotate(false));
}
case mouseRight -> {
System.out.println("mouse right");
System.out.println(this.tetrisGame.rotate(true));
}
case space -> System.out.println("space"); case space -> System.out.println("space");
} }
} }

View File

@ -8,34 +8,61 @@ import net.minestom.server.instance.block.Block;
public class Playfield { public class Playfield {
private final Pos lowerLeftCorner; private final Pos lowerLeftCorner;
private final StatelessGame instance;
public Playfield(Pos lowerLeftCorner) { public Playfield(Pos lowerLeftCorner, StatelessGame instance) {
this.lowerLeftCorner = lowerLeftCorner; this.lowerLeftCorner = lowerLeftCorner;
this.instance = instance;
} }
public Pos getPlayerSpawnPosition() { public Pos getPlayerSpawnPosition() {
return lowerLeftCorner.add(6, 8, 15); return this.lowerLeftCorner.add(6, 8, 15);
} }
public Pos getTetrominoSpawnPosition() { public Pos getTetrominoSpawnPosition() {
return this.lowerLeftCorner.add(5, 21, 1); return this.lowerLeftCorner.add(5, 21, 1);
} }
public void generate(StatelessGame instance) { public void generate() {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
for(int x=0; x<12; x++) { for(int x=0; x<12; x++) {
for(int y=0; y<21; y++) { for(int y=0; y<21; y++) {
batch.setBlock(lowerLeftCorner.add(x, y, 0), Block.STONE); batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.STONE);
if(x==0 || x==11 || y==0) { if(x==0 || x==11 || y==0) {
batch.setBlock(lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE); batch.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE);
} }
} }
} }
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
BatchUtil.loadAndApplyBatch(batch, instance, () -> {}); BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
}
public int removeFullLines() {
int removedLinesCounter = 0;
for(int y=1; y<21; y++) {
boolean isFullLine = true;
for(int x=1; x<11; x++) {
if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false;
}
if(isFullLine) {
removeFullLine(y);
removedLinesCounter += 1;
y -= 1;
}
}
return removedLinesCounter;
}
private void removeFullLine(int positionY) {
for(int y=positionY; y<21; y++) {
for(int x=1; x<11; x++) {
Block blockAbove = this.instance.getBlock(this.lowerLeftCorner.add(x, y+1, 1));
this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockAbove);
}
}
} }
} }

View File

@ -11,10 +11,13 @@ import java.util.Random;
public class TetrisGame { public class TetrisGame {
private final StatelessGame instance; private final StatelessGame instance;
private final Playfield playfield; private final Playfield playfield;
private int speed = 1; private int level = 1;
private int lines = 0; private int lines = 0;
private int score = 0;
private boolean lost = false;
public Tetromino currentTetromino; public Tetromino currentTetromino;
private Tetromino nextTetromino; private Tetromino nextTetromino;
private Tetromino holdTetromino;
private final Random random = new Random(); private final Random random = new Random();
private final Pos tetrominoSpawnPosition; private final Pos tetrominoSpawnPosition;
@ -24,10 +27,10 @@ public class TetrisGame {
} }
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) { public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) {
this.playfield = new Playfield(lowerLeftCorner); this.instance = instance;
this.playfield = new Playfield(lowerLeftCorner, this.instance);
this.currentTetromino = startTetromino; this.currentTetromino = startTetromino;
this.nextTetromino = nextTetromino; this.nextTetromino = nextTetromino;
this.instance = instance;
this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition(); this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition();
} }
@ -35,28 +38,53 @@ public class TetrisGame {
public void start() { public void start() {
Scheduler scheduler = MinecraftServer.getSchedulerManager(); Scheduler scheduler = MinecraftServer.getSchedulerManager();
scheduler.submitTask(() -> { scheduler.submitTask(() -> {
if(this.lost) return TaskSchedule.stop();
this.tick(); this.tick();
return TaskSchedule.tick(40/this.speed); return TaskSchedule.tick(40/this.level);
}); });
} }
public void generate() { public void generate() {
this.playfield.generate(this.instance); this.playfield.generate();
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
} }
public void tick() { public void tick() {
if(this.lost) return;
if(!currentTetromino.moveDown()) { if(!currentTetromino.moveDown()) {
currentTetromino = nextTetromino; setActiveTetrominoDown();
currentTetromino.setPosition(this.tetrominoSpawnPosition);
currentTetromino.draw();
nextTetromino = getNextTetromino();
} }
} }
public boolean rotate(boolean clockwise) {
if(this.lost) return false;
return this.currentTetromino.rotate(clockwise);
}
public boolean moveLeft() {
if(this.lost) return false;
return this.currentTetromino.moveLeft();
}
public boolean moveRight() {
if(this.lost) return false;
return this.currentTetromino.moveRight();
}
public boolean moveDown() {
if(this.lost) return false;
if(!this.currentTetromino.moveDown()) {
this.setActiveTetrominoDown();
return false;
}
this.score += 1;
return true;
}
private Tetromino getNextTetromino() { private Tetromino getNextTetromino() {
int randomNumber = random.nextInt(1, 7); int randomNumber = random.nextInt(1, 7);
Tetromino.Shape nextShape; Tetromino.Shape nextShape;
@ -73,4 +101,39 @@ public class TetrisGame {
return new Tetromino(this.instance, nextShape); return new Tetromino(this.instance, nextShape);
} }
private void loose() {
this.lost = true;
}
private void setActiveTetrominoDown() {
currentTetromino = nextTetromino;
nextTetromino = getNextTetromino();
int removedLines = this.playfield.removeFullLines();
switch (removedLines) {
case 1 -> {
this.lines += 1;
this.score += 40 * this.level;
}
case 2 -> {
this.lines += 3;
this.score += 100 * this.level;
}
case 3 -> {
this.lines += 5;
this.score += 300 * this.level;
}
case 4 -> {
this.lines += 8;
this.score += 1200 * this.level;
}
}
currentTetromino.setPosition(this.tetrominoSpawnPosition);
currentTetromino.draw();
if(!currentTetromino.moveDown()) {
loose();
}
}
} }

View File

@ -5,6 +5,7 @@ import net.minestom.server.coordinate.Pos;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class Tetromino { public class Tetromino {
@ -95,14 +96,16 @@ public class Tetromino {
if(!clockwise) iterations = 3; if(!clockwise) iterations = 3;
int arrayLength = this.shapeArray.length; int arrayLength = this.shapeArray.length;
int[][] startArray = Arrays.stream(this.shapeArray).map(int[]::clone).toArray(int[][]::new);
int[][] returnArray = new int[arrayLength][arrayLength]; int[][] returnArray = new int[arrayLength][arrayLength];
for(int k=0; k<iterations; k++) { for(int k=0; k<iterations; k++) {
for(int i=0; i<arrayLength; i++) { for(int i=0; i<arrayLength; i++) {
for(int j=0; j<arrayLength; j++) { for(int j=0; j<arrayLength; j++) {
returnArray[i][arrayLength-1-j] = this.shapeArray[j][i]; returnArray[i][arrayLength-1-j] = startArray[j][i];
} }
} }
startArray = Arrays.stream(returnArray).map(int[]::clone).toArray(int[][]::new);
} }
return returnArray; return returnArray;
@ -114,20 +117,27 @@ public class Tetromino {
return true; return true;
} }
} }
System.out.println("Not part of Tetromino!!!");
return false; return false;
} }
private List<Pos> getBlockPositions() { private List<Pos> getBlockPositions() {
List<Pos> returnList = new ArrayList<>(); return this.getBlockPositions(this.position, this.shapeArray);
if(this.position == null) return returnList; }
int arrayLength = this.shapeArray.length; private List<Pos> getBlockPositions(Pos position, int[][] shapeArray) {
List<Pos> returnList = new ArrayList<>();
if(position == null) return returnList;
int arrayLength = shapeArray.length;
for(int x=0; x<arrayLength; x++) { for(int x=0; x<arrayLength; x++) {
for(int y=0; y<arrayLength; y++) { for(int y=0; y<arrayLength; y++) {
if(this.shapeArray[arrayLength-1-y][x] == 1) { if(shapeArray[arrayLength-1-y][x] == 1) {
returnList.add(this.position.add(x-1, y-1, 0)); switch (this.shape) {
case I -> returnList.add(position.add(x-1, y-2, 0));
case O -> returnList.add(position.add(x, y, 0));
default -> returnList.add(position.add(x-1, y-1, 0));
}
} }
} }
} }
@ -136,25 +146,20 @@ public class Tetromino {
} }
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) { private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
int arrayLength = newShapeArray.length; List<Pos> newBlockPositions = getBlockPositions(newPosition, newShapeArray);
for(int x=0; x<arrayLength; x++) { for(Pos pos : newBlockPositions) {
for(int y=0; y<arrayLength; y++) { if(isPartOfTetromino(pos)) continue;
Pos checkPosition = newPosition.add(x-1, y-1, 0); if(this.instance.getBlock(pos) != Block.AIR) return true;
if(isPartOfTetromino(checkPosition)) continue;
if(this.instance.getBlock(checkPosition) != Block.AIR) return true;
}
} }
// TODO: collision check to -y not working
return false; return false;
} }
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) { private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!checkCollision(newPosition, newShapeArray)) { if(!checkCollision(newPosition, newShapeArray)) {
this.remove(); this.remove();
this.shapeArray = newShapeArray; this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
this.setPosition(newPosition); this.setPosition(newPosition);
this.draw(); this.draw();
return true; return true;