added ghost tetromino

This commit is contained in:
Lars Neuhaus 2024-10-22 00:36:57 +02:00
parent f56023004a
commit 710838645f
4 changed files with 78 additions and 37 deletions

View File

@ -93,21 +93,20 @@ class Tetris extends StatelessGame {
@Override @Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) { protected void onLoad(@NotNull CompletableFuture<Void> callback) {
this.tetrisGame = new TetrisGame(this, getSpawn().sub(6, 8, 15));
this.tetrisGame.generate();
} }
@Override @Override
protected void onPlayerMove(@NotNull PlayerMoveEvent event) { protected void onPlayerMove(@NotNull PlayerMoveEvent event) {
// if(!getSpawn().withView(event.getNewPosition()).equals(event.getNewPosition())) {
// event.setCancelled(true);
// event.getPlayer().setSprinting(false);
// }
Player player = event.getPlayer(); Player player = event.getPlayer();
Pos previousPosition = event.getPlayer().getPosition(); Pos previousPosition = event.getPlayer().getPosition();
Pos currentPosition = event.getNewPosition(); Pos currentPosition = event.getNewPosition();
if(this.tetrisGame == null) return;
event.setNewPosition(this.tetrisGame.getPlayerSpawnPosition().withView(event.getNewPosition()));
player.setSprinting(false);
Vec movementVector = currentPosition.asVec().sub(previousPosition.asVec()); Vec movementVector = currentPosition.asVec().sub(previousPosition.asVec());
float yaw = player.getPosition().yaw(); float yaw = player.getPosition().yaw();
@ -145,8 +144,6 @@ class Tetris extends StatelessGame {
} }
if(previousPosition.y() < currentPosition.y()) pressedButton(Button.space); if(previousPosition.y() < currentPosition.y()) pressedButton(Button.space);
// event.setNewPosition(getSpawn().withView(event.getNewPosition()));
} }
protected void onPlayerInteract(@NotNull PlayerUseItemEvent event) { protected void onPlayerInteract(@NotNull PlayerUseItemEvent event) {
@ -162,11 +159,9 @@ class Tetris extends StatelessGame {
p.getInventory().setItemStack(0, ItemStack.builder(Material.BIRCH_BUTTON).customName(Component.text("Controller")).build()); p.getInventory().setItemStack(0, ItemStack.builder(Material.BIRCH_BUTTON).customName(Component.text("Controller")).build());
p.setSprinting(false); p.setSprinting(false);
// p.setGameMode(GameMode.SPECTATOR); this.tetrisGame = new TetrisGame(this, getSpawn().sub(6, 8, 15));
// Entity anvil = new Entity(EntityType.FALLING_BLOCK); this.tetrisGame.generate();
// ((FallingBlockMeta) anvil.getEntityMeta()).setBlock(Block.ANVIL); p.teleport(this.tetrisGame.getPlayerSpawnPosition());
// anvil.setInstance(this, getSpawn());
// anvil.addPassenger(p);
this.tetrisGame.sidebar.addViewer(p); this.tetrisGame.sidebar.addViewer(p);
return super.onPlayerJoin(p); return super.onPlayerJoin(p);

View File

@ -9,6 +9,7 @@ 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; private final StatelessGame instance;
private final int height = 22;
public Playfield(Pos lowerLeftCorner, StatelessGame instance) { public Playfield(Pos lowerLeftCorner, StatelessGame instance) {
this.lowerLeftCorner = lowerLeftCorner; this.lowerLeftCorner = lowerLeftCorner;
@ -16,7 +17,7 @@ public class Playfield {
} }
public Pos getPlayerSpawnPosition() { public Pos getPlayerSpawnPosition() {
return this.lowerLeftCorner.add(6, 8, 15); return this.lowerLeftCorner.add(6, 8, 20);
} }
public Pos getTetrominoSpawnPosition() { public Pos getTetrominoSpawnPosition() {
@ -27,28 +28,36 @@ public class Playfield {
return this.lowerLeftCorner.add(-4, 19, 0); return this.lowerLeftCorner.add(-4, 19, 0);
} }
public Pos getNextPosition() {
return this.lowerLeftCorner.add(14, 19, 0);
}
public void generate() { 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<22; y++) { for(int y=0; y<this.height; y++) {
batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GLASS); batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GLASS);
batch.setBlock(this.lowerLeftCorner.add(x, y, -1), Block.BLACK_CONCRETE); batch.setBlock(this.lowerLeftCorner.add(x, y, -1), Block.BLACK_CONCRETE);
if(x==0 || x==11 || y==0) { if(x==0 || x==11 || y==0) {
batch.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE); batch.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE);
batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GRAY_CONCRETE);
} }
} }
} }
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {}); BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
} }
public int removeFullLines() { public int removeFullLines() {
int removedLinesCounter = 0; int removedLinesCounter = 0;
for(int y=1; y<22; y++) { for(int y=1; y<this.height; y++) {
boolean isFullLine = true; boolean isFullLine = true;
for(int x=1; x<11; x++) { for(int x=1; x<11; x++) {
if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false; if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false;
@ -62,8 +71,19 @@ public class Playfield {
return removedLinesCounter; return removedLinesCounter;
} }
public void removeBlock(Block block) {
for(int x=0; x<12; x++) {
for(int y=0; y<this.height; y++) {
if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)).equals(block)) {
this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.AIR);
}
}
}
}
private void removeFullLine(int positionY) { private void removeFullLine(int positionY) {
for(int y=positionY; y<22; y++) { for(int y=positionY; y<this.height; y++) {
for(int x=1; x<11; x++) { for(int x=1; x<11; x++) {
Block blockAbove = this.instance.getBlock(this.lowerLeftCorner.add(x, y+1, 1)); Block blockAbove = this.instance.getBlock(this.lowerLeftCorner.add(x, y+1, 1));
this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockAbove); this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockAbove);

View File

@ -25,23 +25,30 @@ public class TetrisGame {
private Tetromino holdTetromino; private Tetromino holdTetromino;
private final List<Tetromino> tetrominoBag = new ArrayList<>(); private final List<Tetromino> tetrominoBag = new ArrayList<>();
private boolean holdPossible = true; private boolean holdPossible = true;
private final Pos nextPosition;
private final Pos holdPosition; private final Pos holdPosition;
private final Pos tetrominoSpawnPosition; private final Pos tetrominoSpawnPosition;
public Sidebar sidebar = new Sidebar(Component.text("Info:")); public Sidebar sidebar = new Sidebar(Component.text("Info:"));
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) { public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) {
this(instance, lowerLeftCorner, new Tetromino(instance, Tetromino.Shape.J), new Tetromino(instance, Tetromino.Shape.T)); this(instance, lowerLeftCorner, Tetromino.Shape.J);
} }
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino startTetromino, Tetromino nextTetromino) { public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino.Shape startTetrominoShape) {
this.instance = instance; this.instance = instance;
this.playfield = new Playfield(lowerLeftCorner, this.instance); this.playfield = new Playfield(lowerLeftCorner, this.instance);
this.currentTetromino = startTetromino;
this.nextTetromino = nextTetromino;
this.holdPosition = this.playfield.getHoldPosition(); this.holdPosition = this.playfield.getHoldPosition();
this.nextPosition = this.playfield.getNextPosition();
this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition(); this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition();
this.buildSidebar(); this.buildSidebar();
this.currentTetromino = new Tetromino(this.instance, startTetrominoShape, this.playfield);
this.nextTetromino = this.getNextTetromino();
}
public Pos getPlayerSpawnPosition() {
return this.playfield.getPlayerSpawnPosition();
} }
public void start() { public void start() {
@ -115,7 +122,7 @@ public class TetrisGame {
} }
this.currentTetromino.remove(); this.currentTetromino.remove();
this.holdTetromino = this.currentTetromino; this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.shape, this.playfield);
this.currentTetromino = newCurrentTetromino; this.currentTetromino = newCurrentTetromino;
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
@ -123,7 +130,7 @@ public class TetrisGame {
if(!this.currentTetromino.moveDown()) loose(); if(!this.currentTetromino.moveDown()) loose();
this.holdTetromino.setPosition(this.holdPosition); this.holdTetromino.setPosition(this.holdPosition);
this.holdTetromino.draw(); this.holdTetromino.draw(false);
this.holdPossible = false; this.holdPossible = false;
return true; return true;
} }
@ -132,12 +139,17 @@ public class TetrisGame {
private Tetromino getNextTetromino() { private Tetromino getNextTetromino() {
if(this.tetrominoBag.isEmpty()) { if(this.tetrominoBag.isEmpty()) {
for(Tetromino.Shape shape : Tetromino.Shape.values()) { for(Tetromino.Shape shape : Tetromino.Shape.values()) {
this.tetrominoBag.add(new Tetromino(this.instance, shape)); this.tetrominoBag.add(new Tetromino(this.instance, shape, this.playfield));
} }
Collections.shuffle(this.tetrominoBag); Collections.shuffle(this.tetrominoBag);
} }
return this.tetrominoBag.removeFirst(); if(this.nextTetromino != null) this.nextTetromino.remove();
Tetromino tetromino = this.tetrominoBag.removeFirst();
tetromino.setPosition(this.nextPosition);
tetromino.draw(false);
return tetromino;
} }
private void loose() { private void loose() {
@ -169,8 +181,8 @@ public class TetrisGame {
} }
private void setActiveTetrominoDown() { private void setActiveTetrominoDown() {
currentTetromino = nextTetromino; this.currentTetromino = this.nextTetromino;
nextTetromino = getNextTetromino(); this.nextTetromino = getNextTetromino();
int removedLines = this.playfield.removeFullLines(); int removedLines = this.playfield.removeFullLines();
switch (removedLines) { switch (removedLines) {
@ -197,9 +209,9 @@ public class TetrisGame {
this.updateSidebar(); this.updateSidebar();
currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
currentTetromino.draw(); this.currentTetromino.draw();
if(!currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
loose(); loose();
} }
} }

View File

@ -9,10 +9,12 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
public class Tetromino { public class Tetromino {
private final Shape shape; public final Shape shape;
private final StatelessGame instance; private final StatelessGame instance;
private final Playfield playfield;
private Pos position; private Pos position;
private int[][] shapeArray; private int[][] shapeArray;
private final Block ghostBlock = Block.ICE;
public enum Shape { public enum Shape {
I, I,
@ -24,9 +26,10 @@ public class Tetromino {
Z Z
} }
public Tetromino(StatelessGame instance, Shape shape) { public Tetromino(StatelessGame instance, Shape shape, Playfield playfield) {
this.instance = instance; this.instance = instance;
this.shape = shape; this.shape = shape;
this.playfield = playfield;
switch (this.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 I -> shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
@ -39,10 +42,6 @@ public class Tetromino {
} }
} }
public Pos getPosition() {
return this.position;
}
public void setPosition(Pos newPosition) { public void setPosition(Pos newPosition) {
this.position = newPosition; this.position = newPosition;
} }
@ -68,6 +67,20 @@ public class Tetromino {
} }
public void draw() { public void draw() {
this.draw(true);
}
public void draw(boolean withGhost) {
if(withGhost) {
this.playfield.removeBlock(this.ghostBlock);
Pos ghostPos = this.position;
while (!checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
ghostPos = ghostPos.sub(0, 1, 0);
}
Pos positionChange = this.position.sub(ghostPos);
getBlockPositions().forEach(pos -> this.instance.setBlock(pos.sub(positionChange), this.ghostBlock));
}
getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock())); getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock()));
} }
@ -150,6 +163,7 @@ public class Tetromino {
for(Pos pos : newBlockPositions) { for(Pos pos : newBlockPositions) {
if(isPartOfTetromino(pos)) continue; if(isPartOfTetromino(pos)) continue;
if(this.instance.getBlock(pos) == this.ghostBlock) continue;
if(this.instance.getBlock(pos) != Block.AIR) return true; if(this.instance.getBlock(pos) != Block.AIR) return true;
} }