13 Commits

Author SHA1 Message Date
d98cebd86f added turtleGame playfield and movement 2025-09-02 22:29:47 +02:00
c87d318421 started turtleGame 2025-09-02 18:22:14 +02:00
13cc6c30b5 highground finished 2025-07-26 19:03:48 +02:00
3dd41979f7 WIP: highground 2025-07-25 23:04:03 +02:00
a4c46bc298 sumo renamed 2025-07-25 20:31:50 +02:00
aaad777f9b added Sumo Minigame 2025-07-25 20:22:45 +02:00
c62c7cfd1a added Sumo Minigame 2025-07-19 01:33:37 +02:00
4575164e80 Merge branch 'develop' into develop-hannes
# Conflicts:
#	build.gradle
#	src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java
2025-07-18 21:09:37 +02:00
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
cc371a9c12 Test game 2024-11-02 21:53:17 +01:00
19 changed files with 676 additions and 58 deletions

View File

@@ -8,14 +8,17 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.Backroo
import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain.AcidRainFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.highGround.HighGroundFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.jumpDive.JumpDiveFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.minerun.MinerunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.spleef.SpleefFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight.StickFightFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.TetrisFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.sumo.SumoFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tntrun.TntRunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.trafficlightrace.TrafficLightRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.TurtleGameFactory;
public enum GameList {
DEATHCUBE(new DeathcubeFactory(), GameType.JUMPNRUN),
@@ -26,13 +29,16 @@ public enum GameList {
BEDWARS(new BedwarsFactory(), GameType.PROTOTYPE),
BACKROOMS(new BackroomsFactory(), GameType.PROTOTYPE),
BOWSPLEEF(new BowSpleefFactory(), GameType.PROTOTYPE),
TURTLEGAME(new TurtleGameFactory(), GameType.PROTOTYPE),
TETRIS(new TetrisFactory(), GameType.OTHER),
TNTRUN(new TntRunFactory(), GameType.OTHER),
ANVILRUN(new AnvilRunFactory(), GameType.PVE),
ACIDRAIN(new AcidRainFactory(), GameType.PVE),
ELYTRARACE(new ElytraRaceFactory(), GameType.PVP),
SPLEEF(new SpleefFactory(), GameType.PVP),
JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN);
JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN),
SUMO(new SumoFactory(), GameType.PVP),
HIGHGROUND(new HighGroundFactory(), GameType.PVP);
private final GameFactory factory;
private final GameType type;
@@ -45,6 +51,6 @@ public enum GameList {
}
public GameType getType() {
return type;
return this.type;
}
}

View File

@@ -26,7 +26,7 @@ public class AnvilRunFactory implements GameFactory {
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 5, 10, 15, 20, 25, 30))
.addOption(new NumericOption("seconds", Material.STICK, TranslatedComponent.byId("optionCommon#seconds"), 10, 30, 60, 90, 120));
.addOption(new NumericOption("seconds", Material.CLOCK, TranslatedComponent.byId("optionCommon#seconds"), 10, 30, 60, 90, 120));
}
@Override

View File

@@ -8,6 +8,7 @@ import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import org.jetbrains.annotations.NotNull;

View File

@@ -0,0 +1,126 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.highGround;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.PointsWinScore;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import io.github.togar2.pvp.events.EntityKnockbackEvent;
import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.events.PrepareAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.instance.InstanceTickEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
class HighGround extends StatelessGame {
private final int radius;
private final int seconds;
private final WeakHashMap<Player, Integer> scoreMap = new WeakHashMap<>();
HighGround(int radius, int seconds) {
super(Dimension.THE_END.key, "highground", new PointsWinScore());
this.radius = radius;
this.seconds = seconds;
this.eventNode().addChild(
CombatFeatures.empty()
.add(CombatFeatures.VANILLA_ATTACK)
.add(CombatFeatures.VANILLA_DAMAGE)
.add(CombatFeatures.VANILLA_KNOCKBACK)
.build()
.createNode()
);
this.eventNode().addListener(
FinalAttackEvent.class,
finalAttackEvent -> finalAttackEvent.setBaseDamage(0)
);
this.eventNode().addListener(PrepareAttackEvent.class, prepareAttackEvent -> {
if(this.isBeforeBeginning){
prepareAttackEvent.setCancelled(true);
}
});
this.eventNode().addListener(
EntityKnockbackEvent.class,
entityKnockbackEvent -> entityKnockbackEvent.setStrength(1.1f)
);
this.eventNode().addListener(InstanceTickEvent.class, instanceTickEvent -> {
if (this.isBeforeBeginning || !this.isRunning) return;
this.getPlayers().forEach(player -> {
if((player.isOnGround() && player.getPosition().y() >= 1) || (!player.isOnGround() && player.getPosition().y() >= 1.5)){
this.scoreMap.put(player, this.scoreMap.get(player) + 1);
player.setLevel(this.scoreMap.get(player) / 20);
player.setExp((this.scoreMap.get(player) % 20) / 20.0f);
}
});
});
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
for (int y = 0; y >= -3; y--) {
int radius = (Math.abs(y) * 5) + this.radius;
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
double distance = new Pos(x, 0, z).distance(0, 0, 0);
if (distance <= radius) {
this.setBlock(x, y, z, y == 0 ? Block.DIAMOND_BLOCK : Block.GRASS_BLOCK);
Pos featurePosition = new Pos(x, y + 1, z);
if(y >= 0 || this.getBlock(featurePosition).isSolid()) continue;
if (this.rnd.nextDouble() < 0.1){
this.setBlock(featurePosition, Block.SHORT_GRASS);
}
if (this.rnd.nextDouble() < 0.01){
this.setBlock(featurePosition, BlockPallet.FLOWER.rnd());
}
}
}
}
}
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
Player player = playerMoveEvent.getPlayer();
if(playerMoveEvent.getNewPosition().y() < -10){
player.teleport(this.getSpawn());
}
}
@Override
protected void start() {
this.getPlayers().forEach(player -> this.scoreMap.put(player, 0));
super.start();
}
@Override
protected void onStart() {
this.setTimeLimit(this.seconds);
}
@Override
protected void onStop() {
this.getPlayers().forEach(player -> this.getScore().insertResult(player, this.scoreMap.get(player)));
}
@Override
public Pos getSpawn() {
double theta = this.rnd.nextDouble() * 2 * Math.PI;
double spawnRadius = this.radius + 5;
double x = spawnRadius * Math.cos(theta);
double z = spawnRadius * Math.sin(theta);
return new Pos(x, 0, z).withLookAt(new Pos(0, 0, 0));
}
}

View File

@@ -0,0 +1,36 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.highGround;
import eu.mhsl.minenet.minigames.instance.game.Game;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager;
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.common.NumericOption;
import eu.mhsl.minenet.minigames.instance.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
import java.util.Map;
public class HighGroundFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_Highground#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_Highground#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 3, 5, 7, 10))
.addOption(new NumericOption("seconds", Material.CLOCK, TranslatedComponent.byId("optionCommon#seconds"), 30, 60, 90, 120));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new HighGround(configuration.get("radius").getAsInt(), configuration.get("seconds").getAsInt()).setParent(parent);
}
}

View File

@@ -0,0 +1,129 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.sumo;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LastWinsScore;
import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.events.PrepareAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.block.Block;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
public class Sumo extends StatelessGame {
int radius;
int health;
int seconds;
int originalRadius;
int timer;
WeakHashMap<Player, Integer> healthMap = new WeakHashMap<>();
public Sumo(int radius, int health, int seconds) {
super(Dimension.OVERWORLD.key, "Sumo", new LastWinsScore());
this.getScore().setIgnoreLastPlayers(1);
this.setTime(6000);
this.setTimeRate(0);
this.radius = radius;
this.health = health;
this.seconds = seconds;
this.originalRadius = radius;
this.timer = seconds;
this.eventNode().addChild(
CombatFeatures.empty()
.add(CombatFeatures.VANILLA_ATTACK)
.add(CombatFeatures.VANILLA_DAMAGE)
.add(CombatFeatures.VANILLA_KNOCKBACK)
.build()
.createNode()
);
this.eventNode().addListener(PrepareAttackEvent.class, prepareAttackEvent -> {
if (this.isBeforeBeginning)
prepareAttackEvent.setCancelled(true);
});
this.eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> {
finalAttackEvent.setBaseDamage(0);
((Player) finalAttackEvent.getTarget()).setHealth(20);
});
}
@Override
protected void start() {
this.getPlayers().forEach(player -> {
this.healthMap.put(player, this.health);
player.setLevel(this.healthMap.get(player));
});
MinecraftServer.getSchedulerManager().scheduleTask(
() -> {
if(this.isBeforeBeginning) return TaskSchedule.seconds(1);
this.timer--;
double percent = (double) this.timer / this.seconds;
int radius = (int) (this.originalRadius * percent);
if (this.radius >= 5) {
this.radius = radius;
this.generatePlatform();
return TaskSchedule.seconds(1);
}
return TaskSchedule.stop();
},
TaskSchedule.seconds(1)
);
super.start();
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
this.generatePlatform();
}
private void generatePlatform() {
int buffer = 5;
for(int x = -this.radius - buffer; x <= this.radius + buffer; x++) {
for(int z = -this.radius - buffer; z <= this.radius + buffer; z++) {
double distance = new Pos(x, 0, z).distance(new Pos(0, 0, 0));
if(distance <= this.radius) {
boolean isEdge = this.radius - 1 < distance;
this.setBlock(x, 0, z, isEdge ? Block.RED_CONCRETE : Block.WHITE_CONCRETE);
} else {
this.setBlock(x, 0, z, Block.AIR);
}
}
}
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
Player player = playerMoveEvent.getPlayer();
if(playerMoveEvent.getNewPosition().y() < -10) {
player.teleport(this.getSpawn());
this.healthMap.put(player, this.healthMap.get(player) - 1);
player.setLevel(this.healthMap.get(player));
if (this.healthMap.get(player) == 0) {
this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR);
}
}
}
@Override
public Pos getSpawn() {
return new Pos(0, 2, 0);
}
}

View File

@@ -0,0 +1,36 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.sumo;
import eu.mhsl.minenet.minigames.instance.game.Game;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager;
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.common.NumericOption;
import eu.mhsl.minenet.minigames.instance.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
import java.util.Map;
public class SumoFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_Sumo#name");
}
public TranslatedComponent description() {
return TranslatedComponent.byId("game_Sumo#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30))
.addOption(new NumericOption("health", Material.GOLDEN_APPLE, TranslatedComponent.byId("game_Sumo#lives"), 1, 2, 3, 4, 5))
.addOption(new NumericOption("seconds", Material.CLOCK, TranslatedComponent.byId("optionCommon#seconds"), 30, 60, 90, 120));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new Sumo(configuration.get("radius").getAsInt(), configuration.get("health").getAsInt(), configuration.get("seconds").getAsInt()).setParent(parent);
}
}

View File

@@ -28,7 +28,7 @@ class Tetris extends StatelessGame {
public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) {
super(Dimension.THE_END.key, "Tetris", new PointsWinScore());
eventNode()
this.eventNode()
.addListener(PlayerUseItemEvent.class, this::onPlayerInteract)
.addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack)
.addListener(PlayerTickEvent.class, this::onPlayerTick);
@@ -58,7 +58,7 @@ class Tetris extends StatelessGame {
protected void onStop() {
this.tetrisGames.forEach((player, tetrisGame) -> {
tetrisGame.loose();
getScore().insertResult(player, tetrisGame.getScore());
this.getScore().insertResult(player, tetrisGame.getScore());
tetrisGame.sidebar.removeViewer(player);
});
}
@@ -106,7 +106,7 @@ class Tetris extends StatelessGame {
TetrisGame tetrisGame = this.tetrisGames.get(player);
if(tetrisGame == null) return;
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);
player.setGameMode(GameMode.SPECTATOR);
player.setInvisible(true);
getScore().insertResult(player, tetrisGame.getScore());
this.getScore().insertResult(player, tetrisGame.getScore());
boolean allGamesLost = this.tetrisGames.values().stream()
.filter(game -> !game.lost)
.toList()
.isEmpty();
if(!setTimeLimit && !allGamesLost) {
if(!this.setTimeLimit && !allGamesLost) {
this.setTimeLimit(90);
setTimeLimit = true;
this.setTimeLimit = true;
}
}
@@ -134,7 +134,7 @@ class Tetris extends StatelessGame {
if(this.tetrisGames.get(p) == null) {
this.tetrisGames.put(p, new TetrisGame(
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,
this.nextTetrominoesCount,
this.isFast,

View File

@@ -74,10 +74,10 @@ public class Playfield {
}
}
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);
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(isFullLine) {
removeFullLine(y);
this.removeFullLine(y);
removedLinesCounter += 1;
y -= 1;
}
@@ -99,10 +99,10 @@ public class Playfield {
}
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++) {
moveAllLinesUp();
this.moveAllLinesUp();
for (int x = 1; x < 11; x++) {
if(x != xPosMissing) {
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() {
if(this.lost || this.paused) return;
if(!currentTetromino.moveDown()) {
setActiveTetrominoDown();
if(!this.currentTetromino.moveDown()) {
this.setActiveTetrominoDown();
}
}
@@ -176,7 +176,7 @@ public class TetrisGame {
}
private boolean switchHold() {
if(!holdPossible) return false;
if(!this.holdPossible) return false;
Tetromino newCurrentTetromino;
if(this.holdTetromino == null) {
@@ -194,7 +194,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) loose();
if(!this.currentTetromino.moveDown()) this.loose();
double xChange = this.holdTetromino.getXChange();
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
@@ -312,7 +312,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) {
loose();
this.loose();
}
}
}

View File

@@ -38,13 +38,13 @@ public class Tetromino {
this.uuid = UUID.randomUUID();
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 J -> 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 O -> shapeArray = new int[][]{{1,1}, {1,1}};
case S -> 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 Z -> shapeArray = new int[][]{{1,1,0}, {0,1,1}, {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 -> this.shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}};
case L -> this.shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}};
case O -> this.shapeArray = new int[][]{{1,1}, {1,1}};
case S -> this.shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}};
case T -> this.shapeArray = new int[][]{{0,1,0}, {1,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) {
int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
return checkCollisionAndMove(this.position, newShapeArray);
return this.checkCollisionAndMove(this.position, newShapeArray);
}
public boolean moveDown() {
Pos newPosition = this.position.sub(0, 1, 0);
return checkCollisionAndMove(newPosition, this.shapeArray);
return this.checkCollisionAndMove(newPosition, this.shapeArray);
}
public boolean moveLeft() {
Pos newPosition = this.position.sub(1, 0, 0);
return checkCollisionAndMove(newPosition, this.shapeArray);
return this.checkCollisionAndMove(newPosition, this.shapeArray);
}
public boolean moveRight() {
Pos newPosition = this.position.add(1, 0, 0);
return checkCollisionAndMove(newPosition, this.shapeArray);
return this.checkCollisionAndMove(newPosition, this.shapeArray);
}
public void draw() {
@@ -83,11 +83,11 @@ public class Tetromino {
public void draw(boolean withGhost) {
if(withGhost) {
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);
}
Pos positionChange = this.position.sub(ghostPos);
getBlockPositions().forEach(pos -> {
this.getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock());
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() {
getBlockPositions().forEach(pos -> {
this.getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock());
ghostBlock.setNoGravity(true);
@@ -222,10 +222,10 @@ public class Tetromino {
}
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
List<Pos> newBlockPositions = getBlockPositions(newPosition, newShapeArray);
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
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) != Block.AIR) return true;
}
@@ -234,7 +234,7 @@ public class Tetromino {
}
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!checkCollision(newPosition, newShapeArray)) {
if(!this.checkCollision(newPosition, newShapeArray)) {
this.remove();
this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
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.game.stateless.StatelessGame;
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.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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() {
super(Dimension.NETHER.key, "Towerdefense", new NoScore());
super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore());
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
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;
}
}

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));
}
}

View File

@@ -0,0 +1,70 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.game.TurtleGameInstance;
import eu.mhsl.minenet.minigames.score.PointsWinScore;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.WeakHashMap;
class TurtleGame extends StatelessGame {
private final boolean firstPerson;
private final Map<Player, TurtleGameInstance> gameInstances = new WeakHashMap<>();
public TurtleGame(boolean firstPerson) {
super(Dimension.OVERWORLD.key, "Tetris", new PointsWinScore());
this.firstPerson = firstPerson;
this.eventNode()
.addListener(PlayerMoveEvent.class, this::onPlayerMove);
}
protected void onPlayerMove(@NotNull PlayerMoveEvent event) {
TurtleGameInstance gameInstance = this.gameInstances.get(event.getPlayer());
gameInstance.getTurtle().setView(event.getPlayer().getPosition().yaw(), gameInstance.getTurtle().getPosition().pitch());
if(this.isRunning()) gameInstance.moveTurtle(event.getPlayer().getPosition().direction());
}
@Override
protected boolean onPlayerJoin(Player p) {
p.getInventory().setItemStack(0, ItemStack.builder(Material.BARRIER).customName(Component.text("Reset")).build());
if(this.gameInstances.get(p) == null) {
this.gameInstances.put(p, new TurtleGameInstance(
this,
this.getSpawn().sub(6, 8, 15).add(this.gameInstances.size()*50, 0, 0)
));
this.gameInstances.get(p).generate();
}
TurtleGameInstance gameInstance = this.gameInstances.get(p);
p.teleport(gameInstance.getPlayerSpawnPosition());
EntityCreature turtle = gameInstance.getTurtle();
MinecraftServer.getSchedulerManager().scheduleNextTick(() -> {
turtle.setInstance(this);
turtle.teleport(gameInstance.getPlayerSpawnPosition());
turtle.addPassenger(p);
turtle.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15);
});
return super.onPlayerJoin(p);
}
@Override
protected void onStart() {
this.gameInstances.forEach(((player, gameInstance) -> gameInstance.moveTurtle(player.getPosition().direction())));
// this.gameInstances.forEach(((player, turtleGameInstance) -> turtleGameInstance.start()));
}
}

View File

@@ -0,0 +1,41 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame;
import eu.mhsl.minenet.minigames.instance.game.Game;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager;
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.common.BoolOption;
import eu.mhsl.minenet.minigames.instance.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
import java.util.Map;
public class TurtleGameFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_TurtleGame#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_TurtleGame#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new BoolOption("firstPerson", Material.SPYGLASS, TranslatedComponent.byId("game_TurtleGame#firstPerson")));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new TurtleGame(configuration.get("firstPerson").getAsBoolean());
}
@Override
public Material symbol() {
return Material.TURTLE_EGG;
}
}

View File

@@ -0,0 +1,53 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.game;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import java.awt.*;
public class TurtleGameInstance {
private final StatelessGame instance;
private final Pos startPosition;
private final EntityCreature turtle = new EntityCreature(EntityType.TURTLE);
public TurtleGameInstance(StatelessGame instance, Pos startPosition) {
this.instance = instance;
this.startPosition = startPosition;
}
public void moveTurtle(Vec direction) {
Vec targetDirection = direction.withY(0).normalize().mul(100);
Pos targetPosition = this.turtle.getPosition().add(targetDirection);
this.turtle.getNavigator().setPathTo(targetPosition);
}
public void generate() {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
for (int z = -1; z < 66; z++) {
for (int x = -1; x < 44; x++) {
if(z < 0 || z >= 65 || x < 0 || x >= 43) {
batch.setBlock(this.startPosition.add(x, 1, z), Block.STONE);
batch.setBlock(this.startPosition.add(x, 2, z), Block.STONE_SLAB);
}
batch.setBlock(this.startPosition.add(x, 0, z), Block.SAND);
}
}
BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
}
public Pos getPlayerSpawnPosition() {
return this.startPosition.add(20, 1, 30);
}
public EntityCreature getTurtle() {
return this.turtle;
}
}

View File

@@ -66,6 +66,8 @@ public class Room extends MineNetInstance implements Spawnable {
p.getInventory().clear();
p.setGameMode(GameMode.ADVENTURE);
p.setInvisible(false);
p.setExp(0);
p.setLevel(0);
rooms.add(room);
players.put(p, room);
MoveInstance.move(p, room);
@@ -88,29 +90,29 @@ public class Room extends MineNetInstance implements Spawnable {
private Room(Player owner) {
super(Dimension.THE_END.key);
this.apiDriven = false;
construct();
setOwner(owner);
this.construct();
this.setOwner(owner);
}
protected Room() {
super(Dimension.THE_END.key);
this.apiDriven = true;
construct();
this.construct();
}
private void construct() {
MinecraftServer.getInstanceManager().registerInstance(this);
setChunkLoader(new AnvilLoader(Resource.LOBBY_MAP.getPath()));
this.setChunkLoader(new AnvilLoader(Resource.LOBBY_MAP.getPath()));
this.gameSelector = new GameSelector();
this.gameSelector.setInstance(this, new Pos(0.5, 50, 19.5));
eventNode().addListener(PlayerBlockBreakEvent.class, CommonEventHandles::cancel);
eventNode().addListener(PlayerDisconnectEvent.class, playerDisconnectEvent -> unsetRoom(playerDisconnectEvent.getPlayer()));
this.eventNode().addListener(PlayerBlockBreakEvent.class, CommonEventHandles::cancel);
this.eventNode().addListener(PlayerDisconnectEvent.class, playerDisconnectEvent -> unsetRoom(playerDisconnectEvent.getPlayer()));
}
public Player getOwner() {
return owner;
return this.owner;
}
public void setOwner(Player newOwner) {
@@ -123,7 +125,7 @@ public class Room extends MineNetInstance implements Spawnable {
if(p != this.owner) return;
getAllMembers().stream()
this.getAllMembers().stream()
.filter(player -> player != p) // exclude the current leaving owner
.findFirst()
.ifPresentOrElse(
@@ -133,8 +135,8 @@ public class Room extends MineNetInstance implements Spawnable {
Room.unsetRoom(p);
new ChatMessage(Icon.ERROR).appendStatic("The room leader left!").send(getAllMembers());
new ChatMessage(Icon.SCIENCE).appendStatic(this.owner.getUsername()).appendStatic(" is the new Leader!").send(getAllMembers().stream().filter(player -> player != this.owner).collect(Collectors.toSet()));
new ChatMessage(Icon.ERROR).appendStatic("The room leader left!").send(this.getAllMembers());
new ChatMessage(Icon.SCIENCE).appendStatic(this.owner.getUsername()).appendStatic(" is the new Leader!").send(this.getAllMembers().stream().filter(player -> player != this.owner).collect(Collectors.toSet()));
new ChatMessage(Icon.SUCCESS).appendStatic("You are now the leader.").send(this.owner);
});
}

View File

@@ -12,7 +12,7 @@ public enum BlockPallet {
STONE(new Block[] {Block.CHISELED_STONE_BRICKS, Block.STONE_BRICKS, Block.POLISHED_ANDESITE, Block.POLISHED_BLACKSTONE, Block.POLISHED_DIORITE}),
WINTER(new Block[] {Block.SNOW_BLOCK, Block.ICE, Block.PACKED_ICE, Block.BLUE_CONCRETE, Block.SEA_LANTERN}),
STREET(new Block[] {Block.BLACK_CONCRETE_POWDER, Block.GRAY_CONCRETE_POWDER, Block.GRAVEL, Block.BLACK_CONCRETE, Block.GRAY_CONCRETE}),
FLOWER(new Block[] {Block.ORANGE_TULIP, Block.PINK_TULIP, Block.RED_TULIP, Block.WHITE_TULIP}),
PRESSURE_PLATES(new Block[] {Block.ACACIA_PRESSURE_PLATE, Block.BIRCH_PRESSURE_PLATE, Block.CRIMSON_PRESSURE_PLATE, Block.JUNGLE_PRESSURE_PLATE, Block.OAK_PRESSURE_PLATE, Block.DARK_OAK_PRESSURE_PLATE, Block.HEAVY_WEIGHTED_PRESSURE_PLATE, Block.HEAVY_WEIGHTED_PRESSURE_PLATE, Block.POLISHED_BLACKSTONE_PRESSURE_PLATE, Block.SPRUCE_PRESSURE_PLATE, Block.STONE_PRESSURE_PLATE, Block.WARPED_PRESSURE_PLATE});
final List<Block> list;

View File

@@ -130,3 +130,12 @@ description;Run away from falling anvils;Renne von fallenden Ambossen davon
ns:game_jumpDive#;;
name;Jump dive;Wassersprung
description;Jump into the water, avoiding already used spots!;Springe ins wasser an stellen, in denen noch niemand zuvor gelandet ist!
;;
ns:game_Sumo#;;
name;Sumo;Sumo
lives;Lives;Leben
description;Knock your enemies off and stay on top!;Versuche deinen Gegner von der Plattform zu schubsen!
;;
ns:game_Highground#;;
name;Highground;Hochburg
description;Stay on the high ground to win!;Bleibe solange wie möglich auf der Hochburg, um zu gewinnen!
Can't render this file because it has a wrong number of fields in line 114.