fixed collision detection, added rotation
This commit is contained in:
parent
1332a42bf6
commit
236d372746
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user