Compare commits

...

4 Commits

Author SHA1 Message Date
edf26785a3 removed accidental code change 2025-04-16 23:45:03 +02:00
4d90f5fc28 added missing 'this' qualifier 2025-04-13 12:59:22 +02:00
343decb05a added this qualifier to tetris classes 2025-04-12 23:53:40 +02:00
36c6c93edb started towerdefense path mechanic 2025-04-11 00:51:43 +02:00
6 changed files with 153 additions and 44 deletions

View File

@ -28,7 +28,7 @@ class Tetris extends StatelessGame {
public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) { public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) {
super(Dimension.THE_END.key, "Tetris", new PointsWinScore()); super(Dimension.THE_END.key, "Tetris", new PointsWinScore());
eventNode() this.eventNode()
.addListener(PlayerUseItemEvent.class, this::onPlayerInteract) .addListener(PlayerUseItemEvent.class, this::onPlayerInteract)
.addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack) .addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack)
.addListener(PlayerTickEvent.class, this::onPlayerTick); .addListener(PlayerTickEvent.class, this::onPlayerTick);
@ -58,7 +58,7 @@ class Tetris extends StatelessGame {
protected void onStop() { protected void onStop() {
this.tetrisGames.forEach((player, tetrisGame) -> { this.tetrisGames.forEach((player, tetrisGame) -> {
tetrisGame.loose(); tetrisGame.loose();
getScore().insertResult(player, tetrisGame.getScore()); this.getScore().insertResult(player, tetrisGame.getScore());
tetrisGame.sidebar.removeViewer(player); tetrisGame.sidebar.removeViewer(player);
}); });
} }
@ -106,7 +106,7 @@ class Tetris extends StatelessGame {
TetrisGame tetrisGame = this.tetrisGames.get(player); TetrisGame tetrisGame = this.tetrisGames.get(player);
if(tetrisGame == null) return; if(tetrisGame == null) return;
if(tetrisGame.lost && player.getGameMode() != GameMode.SPECTATOR) { if(tetrisGame.lost && player.getGameMode() != GameMode.SPECTATOR) {
letPlayerLoose(player); this.letPlayerLoose(player);
} }
} }
@ -114,15 +114,15 @@ class Tetris extends StatelessGame {
TetrisGame tetrisGame = this.tetrisGames.get(player); TetrisGame tetrisGame = this.tetrisGames.get(player);
player.setGameMode(GameMode.SPECTATOR); player.setGameMode(GameMode.SPECTATOR);
player.setInvisible(true); player.setInvisible(true);
getScore().insertResult(player, tetrisGame.getScore()); this.getScore().insertResult(player, tetrisGame.getScore());
boolean allGamesLost = this.tetrisGames.values().stream() boolean allGamesLost = this.tetrisGames.values().stream()
.filter(game -> !game.lost) .filter(game -> !game.lost)
.toList() .toList()
.isEmpty(); .isEmpty();
if(!setTimeLimit && !allGamesLost) { if(!this.setTimeLimit && !allGamesLost) {
this.setTimeLimit(90); this.setTimeLimit(90);
setTimeLimit = true; this.setTimeLimit = true;
} }
} }
@ -134,7 +134,7 @@ class Tetris extends StatelessGame {
if(this.tetrisGames.get(p) == null) { if(this.tetrisGames.get(p) == null) {
this.tetrisGames.put(p, new TetrisGame( this.tetrisGames.put(p, new TetrisGame(
this, this,
getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*30, 0, 0), this.getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*30, 0, 0),
Tetromino.Shape.J, Tetromino.Shape.J,
this.nextTetrominoesCount, this.nextTetrominoesCount,
this.isFast, this.isFast,

View File

@ -74,10 +74,10 @@ public class Playfield {
} }
} }
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE); batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE); batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE); batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE); batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);
BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {}); BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
} }
@ -90,7 +90,7 @@ public class Playfield {
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;
} }
if(isFullLine) { if(isFullLine) {
removeFullLine(y); this.removeFullLine(y);
removedLinesCounter += 1; removedLinesCounter += 1;
y -= 1; y -= 1;
} }
@ -99,10 +99,10 @@ public class Playfield {
} }
public void addLines(int lines) { public void addLines(int lines) {
int xPosMissing = random.nextInt(1, 10); int xPosMissing = this.random.nextInt(1, 10);
for (int i = 0; i < lines; i++) { for (int i = 0; i < lines; i++) {
moveAllLinesUp(); this.moveAllLinesUp();
for (int x = 1; x < 11; x++) { for (int x = 1; x < 11; x++) {
if(x != xPosMissing) { if(x != xPosMissing) {
this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE); this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE);

View File

@ -121,8 +121,8 @@ public class TetrisGame {
public void tick() { public void tick() {
if(this.lost || this.paused) return; if(this.lost || this.paused) return;
if(!currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
setActiveTetrominoDown(); this.setActiveTetrominoDown();
} }
} }
@ -176,7 +176,7 @@ public class TetrisGame {
} }
private boolean switchHold() { private boolean switchHold() {
if(!holdPossible) return false; if(!this.holdPossible) return false;
Tetromino newCurrentTetromino; Tetromino newCurrentTetromino;
if(this.holdTetromino == null) { if(this.holdTetromino == null) {
@ -194,7 +194,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) loose(); if(!this.currentTetromino.moveDown()) this.loose();
double xChange = this.holdTetromino.getXChange(); double xChange = this.holdTetromino.getXChange();
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0)); this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
@ -312,7 +312,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
loose(); this.loose();
} }
} }
} }

View File

@ -38,13 +38,13 @@ public class Tetromino {
this.uuid = UUID.randomUUID(); this.uuid = UUID.randomUUID();
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 -> this.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 J -> this.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 L -> this.shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}};
case O -> shapeArray = new int[][]{{1,1}, {1,1}}; case O -> this.shapeArray = new int[][]{{1,1}, {1,1}};
case S -> shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}}; case S -> this.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 T -> this.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}}; case Z -> this.shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}};
} }
} }
@ -58,22 +58,22 @@ public class Tetromino {
public boolean rotate(boolean clockwise) { public boolean rotate(boolean clockwise) {
int[][] newShapeArray = this.getTurnedShapeArray(clockwise); int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
return checkCollisionAndMove(this.position, newShapeArray); return this.checkCollisionAndMove(this.position, newShapeArray);
} }
public boolean moveDown() { public boolean moveDown() {
Pos newPosition = this.position.sub(0, 1, 0); Pos newPosition = this.position.sub(0, 1, 0);
return checkCollisionAndMove(newPosition, this.shapeArray); return this.checkCollisionAndMove(newPosition, this.shapeArray);
} }
public boolean moveLeft() { public boolean moveLeft() {
Pos newPosition = this.position.sub(1, 0, 0); Pos newPosition = this.position.sub(1, 0, 0);
return checkCollisionAndMove(newPosition, this.shapeArray); return this.checkCollisionAndMove(newPosition, this.shapeArray);
} }
public boolean moveRight() { public boolean moveRight() {
Pos newPosition = this.position.add(1, 0, 0); Pos newPosition = this.position.add(1, 0, 0);
return checkCollisionAndMove(newPosition, this.shapeArray); return this.checkCollisionAndMove(newPosition, this.shapeArray);
} }
public void draw() { public void draw() {
@ -83,11 +83,11 @@ public class Tetromino {
public void draw(boolean withGhost) { public void draw(boolean withGhost) {
if(withGhost) { if(withGhost) {
Pos ghostPos = this.position; Pos ghostPos = this.position;
while (!checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) { while (!this.checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
ghostPos = ghostPos.sub(0, 1, 0); ghostPos = ghostPos.sub(0, 1, 0);
} }
Pos positionChange = this.position.sub(ghostPos); Pos positionChange = this.position.sub(ghostPos);
getBlockPositions().forEach(pos -> { this.getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType); Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock()); ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock());
ghostBlock.setNoGravity(true); ghostBlock.setNoGravity(true);
@ -97,11 +97,11 @@ public class Tetromino {
}); });
} }
getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock())); this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock()));
} }
public void drawAsEntities() { public void drawAsEntities() {
getBlockPositions().forEach(pos -> { this.getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType); Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock()); ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock());
ghostBlock.setNoGravity(true); ghostBlock.setNoGravity(true);
@ -222,10 +222,10 @@ public class Tetromino {
} }
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) { private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
List<Pos> newBlockPositions = getBlockPositions(newPosition, newShapeArray); List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
for(Pos pos : newBlockPositions) { for(Pos pos : newBlockPositions) {
if(isPartOfTetromino(pos)) continue; if(this.isPartOfTetromino(pos)) continue;
if(this.instance.getBlock(pos) == this.getGhostBlock()) continue; if(this.instance.getBlock(pos) == this.getGhostBlock()) continue;
if(this.instance.getBlock(pos) != Block.AIR) return true; if(this.instance.getBlock(pos) != Block.AIR) return true;
} }
@ -234,7 +234,7 @@ public class Tetromino {
} }
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) { private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!checkCollision(newPosition, newShapeArray)) { if(!this.checkCollision(newPosition, newShapeArray)) {
this.remove(); this.remove();
this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new); this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
this.setPosition(newPosition); this.setPosition(newPosition);

View File

@ -3,23 +3,74 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator; import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
import eu.mhsl.minenet.minigames.score.NoScore; import eu.mhsl.minenet.minigames.score.LastWinsScore;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.item.ItemStack; import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.item.Material; import net.minestom.server.instance.block.Block;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Towerdefense extends StatelessGame { public class Towerdefense extends StatelessGame {
private final Random random = new Random();
private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
private final List<Pos> mazePath = new ArrayList<>();
private List<TowerdefenseRoom> instances = new ArrayList<>();
public Towerdefense() { public Towerdefense() {
super(Dimension.NETHER.key, "Towerdefense", new NoScore()); super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore());
setGenerator(new MazeGenerator()); setGenerator(new MazeGenerator());
this.generateMaze();
}
private void generateMaze() {
Pos position = new Pos(0, 0, 0);
this.addMazePosition(position, Block.GREEN_WOOL);
List<Integer> previousDirections = new ArrayList<>();
int direction = 1; // 0 -> right; 1 -> straight; 2 -> left
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 3; j++) {
position = position.add(direction-1,0,direction%2);
this.addMazePosition(position, Block.WHITE_WOOL);
}
int origin = 0;
int bound = 3;
long rightLeftDifference = previousDirections.stream().filter(integer -> integer == 0).count() - previousDirections.stream().filter(integer -> integer == 2).count();
if(rightLeftDifference >= 2 || direction == 2) origin = 1;
if(rightLeftDifference <= -2 || direction == 0) bound = 2;
direction = this.random.nextInt(origin, bound);
previousDirections.add(direction);
}
this.addMazePosition(position, Block.WHITE_WOOL);
this.addMazePosition(position.add(0,0,1), Block.WHITE_WOOL);
this.addMazePosition(position.add(0,0,2), Block.RED_WOOL);
}
private void addMazePosition(Pos position, Block pathBlock) {
this.mazeBatch.setBlock(position, pathBlock);
this.mazePath.add(position.add(0.5,1,0.5));
}
public AbsoluteBlockBatch getMazeBatch() {
return this.mazeBatch;
}
public List<Pos> getMazePath() {
return this.mazePath;
} }
@Override @Override
protected boolean onPlayerJoin(Player p) { protected boolean onPlayerJoin(Player p) {
p.getInventory().setItemStack(1, ItemStack.of(Material.ARMOR_STAND)); TowerdefenseRoom newRoom = new TowerdefenseRoom(p, this);
this.instances.add(newRoom);
p.setInstance(newRoom);
newRoom.startWave(List.of(EntityType.ENDERMAN, EntityType.BLAZE, EntityType.PLAYER, EntityType.HORSE, EntityType.ARMOR_STAND, EntityType.SKELETON));
return false; return false;
} }
} }

View File

@ -0,0 +1,58 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.*;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.timer.TaskSchedule;
import java.util.List;
import java.util.UUID;
public class TowerdefenseRoom extends InstanceContainer {
private final Player player;
private final Towerdefense game;
public TowerdefenseRoom(Player player, Towerdefense game) {
super(UUID.randomUUID(), Dimension.OVERWORLD.key);
MinecraftServer.getInstanceManager().registerInstance(this);
this.player = player;
this.game = game;
this.player.setGameMode(GameMode.ADVENTURE);
this.player.setAllowFlying(true);
this.player.getInventory().setItemStack(0, ItemStack.of(Material.ARMOR_STAND));
setGenerator(new MazeGenerator());
BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {});
}
public void startWave(List<EntityType> entities) {
int counter = 0;
for(EntityType entityType : entities) {
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
this.addEntity(new EntityCreature(entityType));
return TaskSchedule.stop();
}, TaskSchedule.millis(800L*counter));
counter++;
}
}
private void addEntity(EntityCreature entity) {
entity.setInstance(this, this.game.getMazePath().getFirst());
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15);
entity.getNavigator().setPathTo(this.game.getMazePath().get(1), 0.7, () -> changeEntityGoal(entity, 1));
}
private void changeEntityGoal(EntityCreature entity, int positionIndex) {
if(positionIndex == this.game.getMazePath().size()-1) {
return;
}
entity.getNavigator().setPathTo(this.game.getMazePath().get(positionIndex+1), 0.7, () -> changeEntityGoal(entity, positionIndex+1));
}
}