added combat system and options for game list
This commit is contained in:
		| @@ -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); | ||||
|                 } | ||||
|                 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); | ||||
|         // 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); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 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); | ||||
|         tetromino.draw(false); | ||||
|         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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user