Compare commits
6 Commits
0b3c757ce0
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| cbec1ea7f8 | |||
| c176cfaf2c | |||
| 56f56d48b6 | |||
| 991e51bc10 | |||
| ffba86ac41 | |||
| f95c670514 |
@@ -7,22 +7,11 @@ public enum Orientation {
|
|||||||
UPSIDE_DOWN;
|
UPSIDE_DOWN;
|
||||||
|
|
||||||
public Orientation rotated(boolean clockwise) {
|
public Orientation rotated(boolean clockwise) {
|
||||||
switch(this) {
|
return switch(this) {
|
||||||
case NONE -> {
|
case NONE -> clockwise ? Orientation.RIGHT : Orientation.LEFT;
|
||||||
return clockwise ? Orientation.RIGHT : Orientation.LEFT;
|
case RIGHT -> clockwise ? Orientation.UPSIDE_DOWN : Orientation.NONE;
|
||||||
}
|
case UPSIDE_DOWN -> clockwise ? Orientation.LEFT : Orientation.RIGHT;
|
||||||
case RIGHT -> {
|
case LEFT -> clockwise ? Orientation.NONE : Orientation.UPSIDE_DOWN;
|
||||||
return clockwise ? Orientation.UPSIDE_DOWN : Orientation.NONE;
|
};
|
||||||
}
|
|
||||||
case UPSIDE_DOWN -> {
|
|
||||||
return clockwise ? Orientation.LEFT : Orientation.RIGHT;
|
|
||||||
}
|
|
||||||
case LEFT -> {
|
|
||||||
return clockwise ? Orientation.NONE : Orientation.UPSIDE_DOWN;
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
return Orientation.NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
public final class RotationChecker {private static final Map<Orientation, int[][]> STANDARD_WALL_KICKS = Map.of(
|
public final class RotationChecker {
|
||||||
|
private static final Map<Orientation, int[][]> STANDARD_WALL_KICKS = Map.of(
|
||||||
Orientation.NONE, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
|
Orientation.NONE, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
|
||||||
Orientation.RIGHT, new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
|
Orientation.RIGHT, new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
|
||||||
Orientation.UPSIDE_DOWN, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
|
Orientation.UPSIDE_DOWN, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ 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.instance.game.stateless.StatelessGame;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.MinecraftServer;
|
|
||||||
import net.minestom.server.coordinate.Pos;
|
import net.minestom.server.coordinate.Pos;
|
||||||
import net.minestom.server.scoreboard.Sidebar;
|
import net.minestom.server.scoreboard.Sidebar;
|
||||||
import net.minestom.server.timer.Scheduler;
|
import net.minestom.server.timer.Task;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -23,6 +22,9 @@ public class TetrisGame {
|
|||||||
private final Map<Button, Long> lastPresses = new HashMap<>();
|
private final Map<Button, Long> lastPresses = new HashMap<>();
|
||||||
private final List<TetrisGame> otherTetrisGames = new ArrayList<>();
|
private final List<TetrisGame> otherTetrisGames = new ArrayList<>();
|
||||||
private final Random random;
|
private final Random random;
|
||||||
|
private Task tetrominoLockTask;
|
||||||
|
private int lockDelayResets;
|
||||||
|
private int currentTetrominoLowestY = Integer.MAX_VALUE;
|
||||||
public boolean lost = false;
|
public boolean lost = false;
|
||||||
public boolean paused = true;
|
public boolean paused = true;
|
||||||
public Tetromino currentTetromino;
|
public Tetromino currentTetromino;
|
||||||
@@ -54,24 +56,27 @@ public class TetrisGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void pressedButton(Button button) {
|
public void pressedButton(Button button) {
|
||||||
final int standardButtonDelay = 100;
|
final int standardButtonDelay = 95;
|
||||||
final int buttonDebounce = 70;
|
final int wsButtonDebounce = 70;
|
||||||
|
|
||||||
if(this.lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis() - standardButtonDelay) return;
|
if(this.lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis() - standardButtonDelay) return;
|
||||||
|
|
||||||
this.lastPresses.put(button, System.currentTimeMillis());
|
switch(button) {
|
||||||
if(button == Button.W) this.lastPresses.put(button, System.currentTimeMillis() + buttonDebounce);
|
case W -> this.lastPresses.put(button, System.currentTimeMillis() + wsButtonDebounce);
|
||||||
if(button == Button.S) this.lastPresses.put(button, System.currentTimeMillis() - buttonDebounce);
|
case S -> this.lastPresses.put(button, System.currentTimeMillis() - wsButtonDebounce);
|
||||||
|
case mouseLeft, mouseRight -> this.lastPresses.put(button, 0L);
|
||||||
|
default -> this.lastPresses.put(button, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
if(this.lost || this.paused) return;
|
if(this.lost || this.paused) return;
|
||||||
|
|
||||||
switch(button) {
|
switch(button) {
|
||||||
case A -> this.currentTetromino.moveLeft();
|
case A -> this.moveLeft();
|
||||||
case S -> this.moveDown();
|
case S -> this.moveDown();
|
||||||
case D -> this.currentTetromino.moveRight();
|
case D -> this.moveRight();
|
||||||
case W -> this.hardDrop();
|
case W -> this.hardDrop();
|
||||||
case mouseLeft -> this.currentTetromino.rotate(false);
|
case mouseLeft -> this.rotate(false);
|
||||||
case mouseRight -> this.currentTetromino.rotate(true);
|
case mouseRight -> this.rotate(true);
|
||||||
case space -> this.switchHold();
|
case space -> this.switchHold();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,8 +87,7 @@ public class TetrisGame {
|
|||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
Scheduler scheduler = MinecraftServer.getSchedulerManager();
|
this.instance.scheduler().submitTask(() -> {
|
||||||
scheduler.submitTask(() -> {
|
|
||||||
if(this.lost) return TaskSchedule.stop();
|
if(this.lost) return TaskSchedule.stop();
|
||||||
int standardTickDelay = 40;
|
int standardTickDelay = 40;
|
||||||
if(this.isFast) standardTickDelay = 20;
|
if(this.isFast) standardTickDelay = 20;
|
||||||
@@ -111,9 +115,12 @@ public class TetrisGame {
|
|||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
if(this.lost || this.paused) return;
|
if(this.lost || this.paused) return;
|
||||||
if(!this.currentTetromino.moveDown()) {
|
if(this.currentTetromino.moveDown()) {
|
||||||
this.setActiveTetrominoDown();
|
this.stopTetrominoLockTask(this.currentTetromino.getYPosition() < this.currentTetrominoLowestY, false);
|
||||||
|
} else {
|
||||||
|
this.scheduleTetrominoLock();
|
||||||
}
|
}
|
||||||
|
this.currentTetrominoLowestY = Math.min(this.currentTetrominoLowestY, this.currentTetromino.getYPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getScore() {
|
public int getScore() {
|
||||||
@@ -139,33 +146,43 @@ public class TetrisGame {
|
|||||||
this.lost = true;
|
this.lost = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean moveDown() {
|
private void moveDown() {
|
||||||
if(!this.currentTetromino.moveDown()) {
|
if(!this.currentTetromino.moveDown()) {
|
||||||
this.setActiveTetrominoDown();
|
this.scheduleTetrominoLock();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
this.stopTetrominoLockTask(this.currentTetromino.getYPosition() < this.currentTetrominoLowestY, false);
|
||||||
this.score += 1;
|
this.score += 1;
|
||||||
this.updateInfo();
|
this.updateInfo();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hardDrop() {
|
private void moveLeft() {
|
||||||
|
if(this.currentTetromino.moveLeft()) this.onSuccessfulMoveOrRotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveRight() {
|
||||||
|
if(this.currentTetromino.moveRight()) this.onSuccessfulMoveOrRotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rotate(boolean clockwise) {
|
||||||
|
if(this.currentTetromino.rotate(clockwise)) this.onSuccessfulMoveOrRotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hardDrop() {
|
||||||
if(!this.currentTetromino.moveDown()) {
|
if(!this.currentTetromino.moveDown()) {
|
||||||
this.setActiveTetrominoDown();
|
this.lockActiveTetromino();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
this.score += 2;
|
do {
|
||||||
this.updateInfo();
|
|
||||||
while(this.currentTetromino.moveDown()) {
|
|
||||||
this.score += 2;
|
this.score += 2;
|
||||||
this.updateInfo();
|
} while(this.currentTetromino.moveDown());
|
||||||
}
|
this.updateInfo();
|
||||||
this.setActiveTetrominoDown();
|
this.lockActiveTetromino();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean switchHold() {
|
private void switchHold() {
|
||||||
if(!this.holdPossible) return false;
|
if(!this.holdPossible) return;
|
||||||
|
this.stopTetrominoLockTask(true);
|
||||||
|
|
||||||
Tetromino newCurrentTetromino;
|
Tetromino newCurrentTetromino;
|
||||||
if(this.holdTetromino == null) {
|
if(this.holdTetromino == null) {
|
||||||
@@ -181,6 +198,7 @@ public class TetrisGame {
|
|||||||
this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.getShape());
|
this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.getShape());
|
||||||
this.currentTetromino = newCurrentTetromino;
|
this.currentTetromino = newCurrentTetromino;
|
||||||
|
|
||||||
|
this.currentTetrominoLowestY = Integer.MAX_VALUE;
|
||||||
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
|
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
|
||||||
this.currentTetromino.draw();
|
this.currentTetromino.draw();
|
||||||
if(!this.currentTetromino.moveDown()) this.loose();
|
if(!this.currentTetromino.moveDown()) this.loose();
|
||||||
@@ -189,7 +207,12 @@ public class TetrisGame {
|
|||||||
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
|
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
|
||||||
this.holdTetromino.drawAsEntities();
|
this.holdTetromino.drawAsEntities();
|
||||||
this.holdPossible = false;
|
this.holdPossible = false;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
private void onSuccessfulMoveOrRotate() {
|
||||||
|
if(!this.currentTetromino.isGrounded()) return;
|
||||||
|
this.stopTetrominoLockTask(false);
|
||||||
|
this.scheduleTetrominoLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNextTetrominoes() {
|
private void updateNextTetrominoes() {
|
||||||
@@ -235,11 +258,39 @@ public class TetrisGame {
|
|||||||
this.sidebar.updateLineScore("2", this.level);
|
this.sidebar.updateLineScore("2", this.level);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setActiveTetrominoDown() {
|
private void scheduleTetrominoLock() {
|
||||||
|
if(this.tetrominoLockTask == null || !this.tetrominoLockTask.isAlive())
|
||||||
|
this.tetrominoLockTask = this.instance.scheduler().scheduleTask(() -> {
|
||||||
|
if(this.currentTetromino.isGrounded()) {
|
||||||
|
this.lockActiveTetromino();
|
||||||
|
} else {
|
||||||
|
this.stopTetrominoLockTask(false, false);
|
||||||
|
}
|
||||||
|
return TaskSchedule.stop();
|
||||||
|
}, TaskSchedule.millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTetrominoLockTask(boolean resetHard) {
|
||||||
|
this.stopTetrominoLockTask(resetHard, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTetrominoLockTask(boolean resetHard, boolean addToResets) {
|
||||||
|
if(resetHard) this.lockDelayResets = 0;
|
||||||
|
|
||||||
|
if(this.lockDelayResets >= 15 && addToResets) {
|
||||||
|
this.lockActiveTetromino();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.tetrominoLockTask != null) {
|
||||||
|
this.tetrominoLockTask.cancel();
|
||||||
|
this.tetrominoLockTask = null;
|
||||||
|
if(!resetHard && addToResets) this.lockDelayResets++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lockActiveTetromino() {
|
||||||
|
this.stopTetrominoLockTask(true);
|
||||||
this.currentTetromino.removeOwnEntities();
|
this.currentTetromino.removeOwnEntities();
|
||||||
this.currentTetromino = this.nextTetrominoes.removeFirst();
|
|
||||||
this.currentTetromino.remove();
|
|
||||||
this.updateNextTetrominoes();
|
|
||||||
|
|
||||||
int removedLines = this.playfield.removeFullLines();
|
int removedLines = this.playfield.removeFullLines();
|
||||||
int combatLines = 0;
|
int combatLines = 0;
|
||||||
@@ -297,6 +348,11 @@ public class TetrisGame {
|
|||||||
|
|
||||||
this.updateInfo();
|
this.updateInfo();
|
||||||
|
|
||||||
|
this.currentTetromino = this.nextTetrominoes.removeFirst();
|
||||||
|
this.currentTetromino.remove();
|
||||||
|
this.updateNextTetrominoes();
|
||||||
|
this.currentTetrominoLowestY = Integer.MAX_VALUE;
|
||||||
|
|
||||||
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
|
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
|
||||||
this.currentTetromino.draw();
|
this.currentTetromino.draw();
|
||||||
if(!this.currentTetromino.moveDown()) {
|
if(!this.currentTetromino.moveDown()) {
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ import net.minestom.server.entity.metadata.other.FallingBlockMeta;
|
|||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.tag.Tag;
|
import net.minestom.server.tag.Tag;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class Tetromino {
|
public class Tetromino {
|
||||||
private final static EntityType ghostEntityType = EntityType.FALLING_BLOCK;
|
private final static EntityType ghostEntityType = EntityType.FALLING_BLOCK;
|
||||||
@@ -44,21 +41,17 @@ public class Tetromino {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(Pos newPosition) {
|
public void setPosition(Pos newPosition) {
|
||||||
this.position = newPosition;
|
this.position = new Pos(newPosition.x(), newPosition.y(), newPosition.z());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean rotate(boolean clockwise) {
|
public boolean rotate(boolean clockwise) {
|
||||||
int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
|
int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
|
||||||
Orientation newOrientation = this.orientation.rotated(clockwise);
|
Orientation newOrientation = this.orientation.rotated(clockwise);
|
||||||
|
|
||||||
// TODO: doesn't work for I-Tetromino
|
|
||||||
int[][] kicksArray = RotationChecker.getKicksArray(this.orientation, newOrientation, this.shape);
|
int[][] kicksArray = RotationChecker.getKicksArray(this.orientation, newOrientation, this.shape);
|
||||||
System.out.println(this.shape.toString() + ": " + Arrays.deepToString(kicksArray));
|
|
||||||
for(int[] k : kicksArray) {
|
for(int[] k : kicksArray) {
|
||||||
Pos candidate = new Pos(this.position.x() + k[0], this.position.y() + k[1], this.position.z());
|
Pos candidate = new Pos(this.position.x() + k[0], this.position.y() + k[1], this.position.z());
|
||||||
boolean moved = this.checkCollisionAndMove(candidate, newShapeArray);
|
if(this.checkCollisionAndMove(candidate, newShapeArray)) {
|
||||||
System.out.println("Candidate: " + Arrays.toString(k) + " " + moved);
|
|
||||||
if(moved) {
|
|
||||||
this.orientation = newOrientation;
|
this.orientation = newOrientation;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -198,7 +191,7 @@ public class Tetromino {
|
|||||||
|
|
||||||
private boolean isPartOfTetromino(Pos position) {
|
private boolean isPartOfTetromino(Pos position) {
|
||||||
return this.getBlockPositions().stream()
|
return this.getBlockPositions().stream()
|
||||||
.anyMatch(pos -> pos.equals(position));
|
.anyMatch(pos -> pos.sameBlock(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Pos> getBlockPositions() {
|
private List<Pos> getBlockPositions() {
|
||||||
@@ -214,9 +207,10 @@ public class Tetromino {
|
|||||||
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(shapeArray[arrayLength - 1 - y][x] == 1) {
|
if(shapeArray[arrayLength - 1 - y][x] == 1) {
|
||||||
switch(this.shape) {
|
if(Objects.requireNonNull(this.shape) == Shape.I) {
|
||||||
case I -> returnList.add(new Pos(position).add(x - 2, y - 2, 0));
|
returnList.add(position.add(x - 2, y - 2, 0));
|
||||||
default -> returnList.add(new Pos(position).add(x - 1, y - 1, 0));
|
} else {
|
||||||
|
returnList.add(position.add(x - 1, y - 1, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,21 +219,6 @@ public class Tetromino {
|
|||||||
return returnList;
|
return returnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private List<Pos> getBlockPositions(Pos position, int[][] shapeArray) {
|
|
||||||
// List<Pos> returnList = new ArrayList<>();
|
|
||||||
// if(position == null) return returnList;
|
|
||||||
//
|
|
||||||
// for(int x = 0; x < shapeArray.length; x++) {
|
|
||||||
// for(int y = 0; y < shapeArray.length; y++) {
|
|
||||||
// if(shapeArray[x][y] == 1) {
|
|
||||||
// returnList.add(new Pos(position).sub(1, 1, 0).add(x, y, 0));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return returnList;
|
|
||||||
// }
|
|
||||||
|
|
||||||
private boolean hasCollision(Pos newPosition, int[][] newShapeArray) {
|
private boolean hasCollision(Pos newPosition, int[][] newShapeArray) {
|
||||||
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
|
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
|
||||||
|
|
||||||
@@ -252,6 +231,10 @@ public class Tetromino {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGrounded() {
|
||||||
|
return this.hasCollision(this.position.sub(0, 1, 0), this.shapeArray);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
|
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
|
||||||
if(this.hasCollision(newPosition, newShapeArray)) return false;
|
if(this.hasCollision(newPosition, newShapeArray)) return false;
|
||||||
this.remove();
|
this.remove();
|
||||||
@@ -261,6 +244,10 @@ public class Tetromino {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getYPosition() {
|
||||||
|
return Math.toIntExact(Math.round(this.position.y()));
|
||||||
|
}
|
||||||
|
|
||||||
public enum Shape {
|
public enum Shape {
|
||||||
I,
|
I,
|
||||||
J,
|
J,
|
||||||
|
|||||||
Reference in New Issue
Block a user