added combat system and options for game list

This commit is contained in:
Lars Neuhaus 2024-10-23 01:08:52 +02:00
parent 075db7a91b
commit 179fa2e4d7
7 changed files with 169 additions and 40 deletions
src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless

@ -25,7 +25,7 @@ public abstract class Option<T> {
this.name = name;
this.options = options;
currentValue = options.get(0);
currentValue = options.getFirst();
}
public void setRestrictionHandler(RestrictionHandler restrictionHandler) {
@ -56,6 +56,10 @@ public abstract class Option<T> {
return Integer.parseInt(getAsString());
}
public boolean getAsBoolean() {
return getAsInt() != 0;
}
public String getAsString() {
return currentValue.toString();
}

@ -6,8 +6,8 @@ import net.minestom.server.item.Material;
import java.util.List;
public class BoolOption extends Option<Boolean> {
public class BoolOption extends Option<Integer> {
public BoolOption(String id, Material item, TranslatedComponent name) {
super(id, item, name, List.of(true, false));
super(id, item, name, List.of(1, 0));
}
}

@ -21,8 +21,11 @@ import java.util.Map;
class Tetris extends StatelessGame {
private final Map<Player, TetrisGame> tetrisGames = new HashMap<>();
private final int nextTetrominoesCount;
private final boolean isFast;
private final boolean hasCombat;
public Tetris() {
public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) {
super(Dimension.THE_END.key, "Tetris", new PointsWinScore());
// this.setGenerator(new CircularPlateTerrainGenerator(20));
@ -30,6 +33,10 @@ class Tetris extends StatelessGame {
.addListener(PlayerUseItemEvent.class, this::onPlayerInteract)
.addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack)
.addListener(PlayerTickEvent.class, this::onPlayerTick);
this.nextTetrominoesCount = nextTetrominoesCount;
this.isFast = isFast;
this.hasCombat = hasCombat;
}
@Override
@ -37,6 +44,11 @@ class Tetris extends StatelessGame {
this.getEntities().stream()
.filter(entity -> entity.getEntityType().equals(Tetromino.getGhostEntityType()))
.forEach(Entity::remove);
if(this.hasCombat) {
this.tetrisGames.forEach((player, tetrisGame) -> tetrisGame.updateOtherTetrisGames(this.tetrisGames.values().stream().toList()));
}
this.tetrisGames.forEach((player, tetrisGame) -> tetrisGame.start());
}
@ -118,8 +130,16 @@ class Tetris extends StatelessGame {
p.setSprinting(false);
if(this.tetrisGames.get(p) == null) {
this.tetrisGames.put(p, new TetrisGame(this, getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*27, 0, 0)));
this.tetrisGames.put(p, new TetrisGame(
this,
getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*24, 0, 0),
Tetromino.Shape.J,
this.nextTetrominoesCount,
this.isFast,
this.hasCombat
));
this.tetrisGames.get(p).generate();
this.tetrisGames.forEach((player, tetrisGame) -> tetrisGame.updateOtherTetrisGames(this.tetrisGames.values().stream().toList()));
}
TetrisGame tetrisGame = this.tetrisGames.get(p);

@ -4,6 +4,8 @@ import eu.mhsl.minenet.minigames.instance.game.Game;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.BoolOption;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.NumericOption;
import eu.mhsl.minenet.minigames.instance.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
@ -23,12 +25,15 @@ public class TetrisFactory implements GameFactory {
@Override
public ConfigManager configuration() {
return new ConfigManager();
return new ConfigManager()
.addOption(new NumericOption("nextTetrominoesCount", Material.LADDER, TranslatedComponent.byId("game_Tetris#nextTetrominoesCount"), 3, 4, 5, 1, 2))
.addOption(new BoolOption("isFast", Material.MINECART, TranslatedComponent.byId("game_Tetris#isFast")))
.addOption(new BoolOption("hasCombat", Material.DIAMOND_SWORD, TranslatedComponent.byId("game_Tetris#hasCombat")));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) {
return new Tetris().setParent(parent);
return new Tetris(configuration.get("nextTetrominoesCount").getAsInt(), configuration.get("isFast").getAsBoolean(), configuration.get("hasCombat").getAsBoolean()).setParent(parent);
}
@Override

@ -8,14 +8,20 @@ import net.minestom.server.entity.EntityType;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import java.util.Random;
public class Playfield {
private final Pos lowerLeftCorner;
private final StatelessGame instance;
private final static int height = 22;
private final int nextTetrominoesCount;
private final Random random;
public Playfield(Pos lowerLeftCorner, StatelessGame instance) {
public Playfield(Pos lowerLeftCorner, StatelessGame instance, int nextTetrominoesCount) {
this.nextTetrominoesCount = nextTetrominoesCount;
this.lowerLeftCorner = lowerLeftCorner;
this.instance = instance;
this.random = new Random();
}
public Pos getPlayerSpawnPosition() {
@ -38,6 +44,7 @@ public class Playfield {
public void generate() {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
// actual playfield:
for(int x=0; x<12; x++) {
for(int y = 0; y< height; y++) {
batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GLASS);
@ -50,16 +57,25 @@ public class Playfield {
}
}
for(int x = 0; x < 6; x++) {
for(int y = 0; y < 6; y++) {
if(x==0 || x==5 || y==0 || y==5) {
batch.setBlock(this.getHoldPosition().add(x-2, y-2, 0), Block.BLACK_CONCRETE);
batch.setBlock(this.getNextPosition().add(x-2, y-2, 0), Block.BLACK_CONCRETE);
batch.setBlock(this.getHoldPosition().add(x-2, y-2, -1), Block.BLACK_CONCRETE);
batch.setBlock(this.getNextPosition().add(x-2, y-2, -1), Block.BLACK_CONCRETE);
// hold position:
for(int x = 0; x < 4; x++) {
for(int y = 0; y < 4; y++) {
// if(x==0 || x==5 || y==0 || y==5) {
// batch.setBlock(this.getHoldPosition().add(x-2, y-2, 0), Block.BLACK_CONCRETE);
// batch.setBlock(this.getHoldPosition().add(x-2, y-2, -1), Block.BLACK_CONCRETE);
// }
batch.setBlock(this.getHoldPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK);
}
batch.setBlock(this.getHoldPosition().add(x-2, y-2, -1), Block.QUARTZ_BLOCK);
batch.setBlock(this.getNextPosition().add(x-2, y-2, -1), Block.QUARTZ_BLOCK);
}
// next positions:
for(int x = 0; x < 4; x++) {
for(int y = -4*this.nextTetrominoesCount+4; y < 4; y++) {
// if(x==0 || x==5 || y==-4*this.nextTetrominoesCount+4 || y==5) {
// batch.setBlock(this.getNextPosition().add(x-2, y-2, 0), Block.BLACK_CONCRETE);
// batch.setBlock(this.getNextPosition().add(x-2, y-2, -1), Block.BLACK_CONCRETE);
// }
batch.setBlock(this.getNextPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK);
}
}
@ -100,6 +116,41 @@ public class Playfield {
.forEach(Entity::remove);
}
public void addLines(int lines) {
int xPosMissing = random.nextInt(1, 10);
for (int i = 0; i < lines; i++) {
moveAllLinesUp();
for (int x = 1; x < 11; x++) {
if(x != xPosMissing) {
this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE);
} else {
this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.AIR);
}
}
}
}
public void updateAttackingLines(int attackingLines) {
for (int y = 0; y < height + 5; y++) {
if(attackingLines > 0) {
this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.REDSTONE_BLOCK);
attackingLines -= 1;
} else {
this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.AIR);
}
}
}
private void moveAllLinesUp() {
for (int y = height + 3; y > 1; y--) {
for (int x = 1; x < 11; x++) {
Block blockBeneath = this.instance.getBlock(this.lowerLeftCorner.add(x, y - 1, 1));
this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockBeneath);
}
}
}
private void removeFullLine(int positionY) {
for(int y = positionY; y< height; y++) {

@ -13,13 +13,16 @@ import java.util.*;
public class TetrisGame {
private final StatelessGame instance;
private final Playfield playfield;
private final boolean isFast;
private int level = 1;
private int lines = 0;
private int score = 0;
private int attackingLines = 0;
public boolean lost = false;
public boolean paused = true;
private final boolean hasCombat;
public Tetromino currentTetromino;
private Tetromino nextTetromino;
private final List<Tetromino> nextTetrominoes = new ArrayList<>();
private Tetromino holdTetromino;
private final List<Tetromino> tetrominoBag = new ArrayList<>();
private boolean holdPossible = true;
@ -28,6 +31,7 @@ public class TetrisGame {
private final Pos tetrominoSpawnPosition;
public Sidebar sidebar = new Sidebar(Component.text("Info:"));
private final Map<Button, Long> lastPresses = new HashMap<>();
private final List<TetrisGame> otherTetrisGames = new ArrayList<>();
public enum Button {
W,
@ -40,12 +44,14 @@ public class TetrisGame {
}
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner) {
this(instance, lowerLeftCorner, Tetromino.Shape.J);
this(instance, lowerLeftCorner, Tetromino.Shape.J, 3, false, false);
}
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino.Shape startTetrominoShape) {
public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino.Shape startTetrominoShape, int nextTetrominoesCount, boolean isfast, boolean hasCombat) {
this.isFast = isfast;
this.hasCombat = hasCombat;
this.instance = instance;
this.playfield = new Playfield(lowerLeftCorner, this.instance);
this.playfield = new Playfield(lowerLeftCorner, this.instance, nextTetrominoesCount);
this.holdPosition = this.playfield.getHoldPosition();
this.nextPosition = this.playfield.getNextPosition();
@ -53,7 +59,9 @@ public class TetrisGame {
this.buildSidebar();
this.currentTetromino = new Tetromino(this.instance, startTetrominoShape, this.playfield);
this.nextTetromino = this.getNextTetromino();
for (int i = 0; i < nextTetrominoesCount; i++) {
this.getNextTetromino();
}
}
public void pressedButton(Button button) {
@ -74,10 +82,6 @@ public class TetrisGame {
}
}
public void releaseButton(Button button) {
this.lastPresses.put(button, 0L);
}
public Pos getPlayerSpawnPosition() {
return this.playfield.getPlayerSpawnPosition();
}
@ -87,9 +91,11 @@ public class TetrisGame {
Scheduler scheduler = MinecraftServer.getSchedulerManager();
scheduler.submitTask(() -> {
if(this.lost) return TaskSchedule.stop();
if(this.paused) return TaskSchedule.tick(40/this.level);
int standardTickDelay = 40;
if(this.isFast) standardTickDelay = 20;
if(this.paused) return TaskSchedule.tick(Math.round((float) standardTickDelay /this.level));
this.tick();
return TaskSchedule.tick(40/this.level);
return TaskSchedule.tick(Math.round((float) standardTickDelay /this.level));
});
}
@ -111,6 +117,20 @@ public class TetrisGame {
return this.score;
}
public void updateOtherTetrisGames(List<TetrisGame> tetrisGames) {
List<TetrisGame> games = new ArrayList<>(tetrisGames);
games.remove(this);
this.otherTetrisGames.clear();
this.otherTetrisGames.addAll(games);
}
public void getAttacked(int lines) {
if(this.hasCombat) {
this.attackingLines += lines;
this.playfield.updateAttackingLines(this.attackingLines);
}
}
private boolean rotate(boolean clockwise) {
if(this.lost || this.paused) return false;
@ -160,8 +180,9 @@ public class TetrisGame {
Tetromino newCurrentTetromino;
if(this.holdTetromino == null) {
newCurrentTetromino = this.nextTetromino;
this.nextTetromino = this.getNextTetromino();
newCurrentTetromino = this.nextTetrominoes.removeFirst();
newCurrentTetromino.remove();
this.getNextTetromino();
} else {
newCurrentTetromino = this.holdTetromino;
this.holdTetromino.remove();
@ -190,12 +211,15 @@ public class TetrisGame {
Collections.shuffle(this.tetrominoBag);
}
if(this.nextTetromino != null) this.nextTetromino.remove();
Tetromino tetromino = this.tetrominoBag.removeFirst();
tetromino.setPosition(this.nextPosition);
if(!this.nextTetrominoes.isEmpty()) this.nextTetrominoes.forEach(Tetromino::remove);
Tetromino newTetromino = this.tetrominoBag.removeFirst();
this.nextTetrominoes.add(newTetromino);
this.nextTetrominoes.forEach(tetromino -> {
tetromino.setPosition(this.nextPosition.sub(0, 4*this.nextTetrominoes.indexOf(tetromino), 0));
tetromino.draw(false);
});
return tetromino;
return newTetromino;
}
private void loose() {
@ -227,29 +251,54 @@ public class TetrisGame {
}
private void setActiveTetrominoDown() {
this.currentTetromino = this.nextTetromino;
this.nextTetromino = getNextTetromino();
this.currentTetromino = this.nextTetrominoes.removeFirst();
this.currentTetromino.remove();
this.getNextTetromino();
int removedLines = this.playfield.removeFullLines();
int combatLines = 0;
switch (removedLines) {
case 1 -> {
this.lines += 1;
this.score += 40 * this.level;
}
case 2 -> {
combatLines = 1;
this.lines += 2;
this.score += 100 * this.level;
}
case 3 -> {
combatLines = 2;
this.lines += 3;
this.score += 300 * this.level;
}
case 4 -> {
combatLines = 4;
this.lines += 4;
this.score += 1200 * this.level;
}
}
if(this.hasCombat && this.attackingLines > 0) {
if(combatLines > 0 && this.attackingLines >= combatLines) {
this.attackingLines -= combatLines;
combatLines = 0;
} else if(combatLines > 0) {
combatLines -= this.attackingLines;
this.attackingLines = 0;
} else {
this.playfield.addLines(this.attackingLines);
this.attackingLines = 0;
}
this.playfield.updateAttackingLines(this.attackingLines);
}
if(this.hasCombat && !this.otherTetrisGames.isEmpty()) {
Collections.shuffle(this.otherTetrisGames);
this.otherTetrisGames.getFirst().getAttacked(combatLines);
}
this.level = (int) Math.floor((double) this.lines / 10) + 1;
this.holdPossible = true;

@ -102,8 +102,7 @@ public class Tetromino {
this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, Block.AIR));
}
private Block getColoredBlock() {
public Block getColoredBlock() {
Block returnBlock;
switch (this.shape) {
case I -> returnBlock = Block.LIGHT_BLUE_CONCRETE;
@ -118,6 +117,7 @@ public class Tetromino {
return returnBlock;
}
private Block getGhostBlock() {
Block returnBlock;
switch (this.shape) {