From c87d31842125f8443581b89581ca6dcd196b1d04 Mon Sep 17 00:00:00 2001 From: lars Date: Tue, 2 Sep 2025 18:22:14 +0200 Subject: [PATCH 01/38] started turtleGame --- .../types/turtleGame/TurtleGame.java | 11 ++++++++++ .../types/turtleGame/TurtleGameFactory.java | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java new file mode 100644 index 0000000..69435c7 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -0,0 +1,11 @@ +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.score.PointsWinScore; + +class TurtleGame extends StatelessGame { + public TurtleGame() { + super(Dimension.OVERWORLD.key, "Tetris", new PointsWinScore()); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java new file mode 100644 index 0000000..0ea7728 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -0,0 +1,22 @@ +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.GameFactory; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option; +import eu.mhsl.minenet.minigames.instance.room.Room; +import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; + +import java.util.Map; + +public class TurtleGameFactory implements GameFactory { + + @Override + public TranslatedComponent name() { + return TranslatedComponent.byId("game_TurtleGame#name"); + } + + @Override + public Game manufacture(Room parent, Map> configuration) throws Exception { + return new TurtleGame(); + } +} From d98cebd86f77273112a03ab18af7fb487a43bcd8 Mon Sep 17 00:00:00 2001 From: lars Date: Tue, 2 Sep 2025 22:29:47 +0200 Subject: [PATCH 02/38] added turtleGame playfield and movement --- .../minigames/instance/game/GameList.java | 2 + .../types/turtleGame/TurtleGame.java | 61 ++++++++++++++++++- .../types/turtleGame/TurtleGameFactory.java | 21 ++++++- .../turtleGame/game/TurtleGameInstance.java | 53 ++++++++++++++++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java index 6fc6d6d..f6236c1 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java @@ -18,6 +18,7 @@ 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), @@ -28,6 +29,7 @@ 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), diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 69435c7..3e93cd5 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -2,10 +2,69 @@ 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 { - public TurtleGame() { + private final boolean firstPerson; + private final Map 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())); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index 0ea7728..d2b9a20 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -1,10 +1,13 @@ 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; @@ -15,8 +18,24 @@ public class TurtleGameFactory implements GameFactory { 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> configuration) throws Exception { - return new TurtleGame(); + return new TurtleGame(configuration.get("firstPerson").getAsBoolean()); + } + + @Override + public Material symbol() { + return Material.TURTLE_EGG; } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java new file mode 100644 index 0000000..38216e9 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java @@ -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; + } +} From abcb23d96ae44458a6b175bb46163db903f8eb01 Mon Sep 17 00:00:00 2001 From: lars Date: Fri, 3 Oct 2025 18:47:11 +0200 Subject: [PATCH 03/38] made turtle movement clean --- .../game/stateless/types/turtleGame/TurtleGame.java | 12 ++++++------ .../types/turtleGame/game/TurtleGameInstance.java | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 3e93cd5..50484c7 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -9,7 +9,7 @@ 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.event.player.PlayerTickEvent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; @@ -22,22 +22,22 @@ class TurtleGame extends StatelessGame { private final Map gameInstances = new WeakHashMap<>(); public TurtleGame(boolean firstPerson) { - super(Dimension.OVERWORLD.key, "Tetris", new PointsWinScore()); + super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.firstPerson = firstPerson; this.eventNode() - .addListener(PlayerMoveEvent.class, this::onPlayerMove); + .addListener(PlayerTickEvent.class, this::onPlayerTick); } - protected void onPlayerMove(@NotNull PlayerMoveEvent event) { + protected void onPlayerTick(@NotNull PlayerTickEvent event) { TurtleGameInstance gameInstance = this.gameInstances.get(event.getPlayer()); - gameInstance.getTurtle().setView(event.getPlayer().getPosition().yaw(), gameInstance.getTurtle().getPosition().pitch()); + gameInstance.getTurtle().teleport(gameInstance.getTurtle().getPosition().withView(event.getPlayer().getPosition())); 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()); + p.getInventory().setItemStack(0, ItemStack.builder(Material.BARRIER).customName(Component.text("Reset Snack")).build()); if(this.gameInstances.get(p) == null) { this.gameInstances.put(p, new TurtleGameInstance( diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java index 38216e9..9bf83d4 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java @@ -15,6 +15,7 @@ public class TurtleGameInstance { private final StatelessGame instance; private final Pos startPosition; private final EntityCreature turtle = new EntityCreature(EntityType.TURTLE); + private double speed = 2; public TurtleGameInstance(StatelessGame instance, Pos startPosition) { this.instance = instance; @@ -22,9 +23,8 @@ public class TurtleGameInstance { } 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); + Vec targetDirection = direction.withY(0).normalize().mul(speed); + this.turtle.setVelocity(targetDirection); } public void generate() { From f2fc4835c305e5ce48934a8ce35109b3f9e12e10 Mon Sep 17 00:00:00 2001 From: lars Date: Fri, 3 Oct 2025 19:32:52 +0200 Subject: [PATCH 04/38] started random snack generation --- .../game/stateless/types/turtleGame/TurtleGame.java | 3 +++ .../stateless/types/turtleGame/TurtleGameFactory.java | 4 +++- .../types/turtleGame/game/TurtleGameInstance.java | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 50484c7..4e49edf 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -15,11 +15,13 @@ import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; import java.util.Map; +import java.util.Random; import java.util.WeakHashMap; class TurtleGame extends StatelessGame { private final boolean firstPerson; private final Map gameInstances = new WeakHashMap<>(); + private final Random random = new Random(); public TurtleGame(boolean firstPerson) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); @@ -39,6 +41,7 @@ class TurtleGame extends StatelessGame { protected boolean onPlayerJoin(Player p) { p.getInventory().setItemStack(0, ItemStack.builder(Material.BARRIER).customName(Component.text("Reset Snack")).build()); + // TODO: instance auflösen, ein großer Kreis if(this.gameInstances.get(p) == null) { this.gameInstances.put(p, new TurtleGameInstance( this, diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index d2b9a20..f3f673a 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -5,6 +5,7 @@ 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.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; @@ -26,7 +27,8 @@ public class TurtleGameFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new BoolOption("firstPerson", Material.SPYGLASS, TranslatedComponent.byId("game_TurtleGame#firstPerson"))); + .addOption(new BoolOption("firstPerson", Material.SPYGLASS, TranslatedComponent.byId("game_TurtleGame#firstPerson"))) + .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java index 9bf83d4..438f8af 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java @@ -27,6 +27,15 @@ public class TurtleGameInstance { this.turtle.setVelocity(targetDirection); } + // TODO: kleine display entities, die rotieren und so +// public void generateNewSnack() { +// int z, x; +// do { +// z = random.nextInt(0, 66); +// x = random.nextInt(0, 44); +// } while (!this.instance.getBlock(this.startPosition.add(x, 1, z)).isAir()); +// } + public void generate() { AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); From 81524cfecf55108781981073e7c13b028392944e Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 00:09:19 +0200 Subject: [PATCH 05/38] added snacks with collision, switched to multiplayer arena --- .../types/turtleGame/TurtleGame.java | 123 ++++++++++++++---- .../types/turtleGame/TurtleGameFactory.java | 2 +- .../turtleGame/game/TurtleGameInstance.java | 62 --------- 3 files changed, 99 insertions(+), 88 deletions(-) delete mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 4e49edf..bc4fbe7 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -2,62 +2,95 @@ 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.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.EntityType; import net.minestom.server.entity.Player; import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.entity.metadata.other.FallingBlockMeta; import net.minestom.server.event.player.PlayerTickEvent; +import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; -import java.util.Map; -import java.util.Random; -import java.util.WeakHashMap; +import java.util.*; +import java.util.concurrent.CompletableFuture; class TurtleGame extends StatelessGame { private final boolean firstPerson; - private final Map gameInstances = new WeakHashMap<>(); - private final Random random = new Random(); + private final int radius; + private final Map turtlePlayerMap = new WeakHashMap<>(); + private final ArrayList snacks = new ArrayList<>(); + private final double speed = 2; - public TurtleGame(boolean firstPerson) { + public TurtleGame(boolean firstPerson, int radius) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.firstPerson = firstPerson; + this.radius = radius; this.eventNode() .addListener(PlayerTickEvent.class, this::onPlayerTick); } + @Override + protected void onLoad(@NotNull CompletableFuture callback) { + this.generatePlatform(); + } + + private void generatePlatform() { + for(int x = -this.radius - 1; x <= this.radius + 1; x++) { + for(int z = -this.radius - 1; z <= this.radius + 1; 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, Block.SAND); + this.setBlock(x, 1, z, isEdge ? Block.STONE : Block.AIR); + this.setBlock(x, 2, z, isEdge ? Block.STONE_SLAB : Block.AIR); + } else { + this.setBlock(x, 0, z, Block.AIR); + } + } + } + } + protected void onPlayerTick(@NotNull PlayerTickEvent event) { - TurtleGameInstance gameInstance = this.gameInstances.get(event.getPlayer()); - gameInstance.getTurtle().teleport(gameInstance.getTurtle().getPosition().withView(event.getPlayer().getPosition())); - if(this.isRunning()) gameInstance.moveTurtle(event.getPlayer().getPosition().direction()); + EntityCreature turtle = this.turtlePlayerMap.get(event.getPlayer()); + turtle.teleport(turtle.getPosition().withView(event.getPlayer().getPosition())); + Vec direction = event.getPlayer().getPosition().direction(); + if(this.isRunning()) { + Vec targetDirection = direction.withY(0).normalize().mul(this.speed); + turtle.setVelocity(targetDirection); + + List hit = this.snacks.stream() + .filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) + .toList(); + hit.forEach(snack -> { + this.snacks.remove(snack); + snack.remove(); + this.generateNewSnack(); + }); + } } @Override protected boolean onPlayerJoin(Player p) { p.getInventory().setItemStack(0, ItemStack.builder(Material.BARRIER).customName(Component.text("Reset Snack")).build()); - // TODO: instance auflösen, ein großer Kreis - 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(); + if(this.turtlePlayerMap.get(p) == null) { + EntityCreature turtle = new EntityCreature(EntityType.TURTLE); + this.turtlePlayerMap.put(p, turtle); } - TurtleGameInstance gameInstance = this.gameInstances.get(p); - - p.teleport(gameInstance.getPlayerSpawnPosition()); - - EntityCreature turtle = gameInstance.getTurtle(); + EntityCreature turtle = this.turtlePlayerMap.get(p); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { turtle.setInstance(this); - turtle.teleport(gameInstance.getPlayerSpawnPosition()); + turtle.teleport(p.getPosition()); turtle.addPassenger(p); turtle.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); }); @@ -65,9 +98,49 @@ class TurtleGame extends StatelessGame { return super.onPlayerJoin(p); } + @Override + protected void onPlayerLeave(Player p) { + EntityCreature turtle = this.turtlePlayerMap.get(p); + turtle.remove(); + } + + @Override + public Pos getSpawn() { + double theta = this.rnd.nextDouble() * 2 * Math.PI; + + double spawnRadius = this.radius - 4; + double x = spawnRadius * Math.cos(theta); + double z = spawnRadius * Math.sin(theta); + + return new Pos(x, 1, z).withLookAt(new Pos(0, 0, 0)); + } + + private void generateNewSnack() { + Pos spawnPosition; + boolean isInRadius, isEmpty; + int counter = 0; + do { + if(counter > 200) return; + int x = this.rnd.nextInt(-this.radius+2, this.radius-2); + int z = this.rnd.nextInt(-this.radius+2, this.radius-2); + spawnPosition = new Pos(x, 1, z); + isInRadius = new Pos(x, 1, z).distance(0, 1, 0) < this.radius-2; + isEmpty = this.snacks.stream().noneMatch(entity -> entity.getPosition().sameBlock(x, 1, z)); + counter++; + } while (!(isEmpty && isInRadius)); + + Entity snack = new Entity(EntityType.FALLING_BLOCK); + FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); + meta.setBlock(Block.CHORUS_FLOWER); + snack.setInstance(this); + snack.teleport(spawnPosition.add(0.5, 0, 0.5)); + this.snacks.add(snack); + } + @Override protected void onStart() { - this.gameInstances.forEach(((player, gameInstance) -> gameInstance.moveTurtle(player.getPosition().direction()))); -// this.gameInstances.forEach(((player, turtleGameInstance) -> turtleGameInstance.start())); + for (int i = 0; i < 3; i++) { + this.generateNewSnack(); + } } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index f3f673a..b47f82a 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -33,7 +33,7 @@ public class TurtleGameFactory implements GameFactory { @Override public Game manufacture(Room parent, Map> configuration) throws Exception { - return new TurtleGame(configuration.get("firstPerson").getAsBoolean()); + return new TurtleGame(configuration.get("firstPerson").getAsBoolean(), configuration.get("radius").getAsInt()); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java deleted file mode 100644 index 438f8af..0000000 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/game/TurtleGameInstance.java +++ /dev/null @@ -1,62 +0,0 @@ -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); - private double speed = 2; - - public TurtleGameInstance(StatelessGame instance, Pos startPosition) { - this.instance = instance; - this.startPosition = startPosition; - } - - public void moveTurtle(Vec direction) { - Vec targetDirection = direction.withY(0).normalize().mul(speed); - this.turtle.setVelocity(targetDirection); - } - - // TODO: kleine display entities, die rotieren und so -// public void generateNewSnack() { -// int z, x; -// do { -// z = random.nextInt(0, 66); -// x = random.nextInt(0, 44); -// } while (!this.instance.getBlock(this.startPosition.add(x, 1, z)).isAir()); -// } - - 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; - } -} From f26c3a9e6d09e519da4745978219d22c6ab6583e Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 00:36:50 +0200 Subject: [PATCH 06/38] improved entity collision check while generating --- .../types/turtleGame/TurtleGame.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index bc4fbe7..71d073f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -117,29 +117,36 @@ class TurtleGame extends StatelessGame { private void generateNewSnack() { Pos spawnPosition; - boolean isInRadius, isEmpty; + boolean isInRadius, collides; int counter = 0; - do { - if(counter > 200) return; - int x = this.rnd.nextInt(-this.radius+2, this.radius-2); - int z = this.rnd.nextInt(-this.radius+2, this.radius-2); - spawnPosition = new Pos(x, 1, z); - isInRadius = new Pos(x, 1, z).distance(0, 1, 0) < this.radius-2; - isEmpty = this.snacks.stream().noneMatch(entity -> entity.getPosition().sameBlock(x, 1, z)); - counter++; - } while (!(isEmpty && isInRadius)); - Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); meta.setBlock(Block.CHORUS_FLOWER); snack.setInstance(this); - snack.teleport(spawnPosition.add(0.5, 0, 0.5)); + + do { + if(counter > 200) { + snack.remove(); + return; + } + int x = this.rnd.nextInt(-this.radius+2, this.radius-2); + int z = this.rnd.nextInt(-this.radius+2, this.radius-2); + spawnPosition = new Pos(x, 1, z).add(0.5, 0, 0.5); + Pos checkPosition = spawnPosition; + isInRadius = checkPosition.distance(0, 1, 0) < this.radius-3; + collides = this.getEntities().stream() + .filter(entity -> !entity.equals(snack)) + .anyMatch(entity -> snack.getBoundingBox().intersectBox(entity.getPosition().sub(checkPosition), entity.getBoundingBox())); + counter++; + } while (!isInRadius || collides); + + snack.teleport(spawnPosition); this.snacks.add(snack); } @Override protected void onStart() { - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 100; i++) { this.generateNewSnack(); } } From 2c92553a8a25f645c46f855da5f571f58b909e9e Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 13:15:57 +0200 Subject: [PATCH 07/38] added bombs --- .../types/turtleGame/TurtleGame.java | 97 ++++++++++++++----- .../types/turtleGame/TurtleGameFactory.java | 4 +- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 71d073f..3b45fb7 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -7,10 +7,7 @@ import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; -import net.minestom.server.entity.Entity; -import net.minestom.server.entity.EntityCreature; -import net.minestom.server.entity.EntityType; -import net.minestom.server.entity.Player; +import net.minestom.server.entity.*; import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.metadata.other.FallingBlockMeta; import net.minestom.server.event.player.PlayerTickEvent; @@ -18,20 +15,21 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; class TurtleGame extends StatelessGame { - private final boolean firstPerson; private final int radius; private final Map turtlePlayerMap = new WeakHashMap<>(); + private final Map scoreMap = new WeakHashMap<>(); private final ArrayList snacks = new ArrayList<>(); - private final double speed = 2; + private final ArrayList bombs = new ArrayList<>(); + private double speed = 2; - public TurtleGame(boolean firstPerson, int radius) { + public TurtleGame(int radius) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); - this.firstPerson = firstPerson; this.radius = radius; this.eventNode() @@ -60,20 +58,39 @@ class TurtleGame extends StatelessGame { } protected void onPlayerTick(@NotNull PlayerTickEvent event) { - EntityCreature turtle = this.turtlePlayerMap.get(event.getPlayer()); - turtle.teleport(turtle.getPosition().withView(event.getPlayer().getPosition())); - Vec direction = event.getPlayer().getPosition().direction(); + Player p = event.getPlayer(); + if(p.getGameMode() == GameMode.SPECTATOR) return; + EntityCreature turtle = this.turtlePlayerMap.get(p); + turtle.teleport(turtle.getPosition().withView(p.getPosition())); + Vec direction = p.getPosition().direction(); if(this.isRunning()) { Vec targetDirection = direction.withY(0).normalize().mul(this.speed); turtle.setVelocity(targetDirection); - List hit = this.snacks.stream() + List hitSnacks = this.snacks.stream() .filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) .toList(); - hit.forEach(snack -> { + List hitBombs = this.bombs.stream() + .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) + .toList(); + + hitSnacks.forEach(snack -> { this.snacks.remove(snack); snack.remove(); this.generateNewSnack(); + this.scoreMap.put(p, this.scoreMap.get(p) + 1); + }); + hitBombs.forEach(bomb -> { + p.setGameMode(GameMode.SPECTATOR); + p.setFlying(true); + turtle.removePassenger(p); + turtle.remove(); + turtle.kill(); + this.bombs.remove(bomb); + bomb.remove(); + this.generateNewBomb(); + this.getScore().insertResult(p, this.scoreMap.get(p)); + this.speed += 0.5; }); } } @@ -86,6 +103,7 @@ class TurtleGame extends StatelessGame { EntityCreature turtle = new EntityCreature(EntityType.TURTLE); this.turtlePlayerMap.put(p, turtle); } + this.scoreMap.putIfAbsent(p, 0); EntityCreature turtle = this.turtlePlayerMap.get(p); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { @@ -116,18 +134,46 @@ class TurtleGame extends StatelessGame { } private void generateNewSnack() { - Pos spawnPosition; - boolean isInRadius, collides; - int counter = 0; Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); - meta.setBlock(Block.CHORUS_FLOWER); + meta.setBlock(Block.SUNFLOWER.withProperty("half", "upper")); snack.setInstance(this); + Pos spawnPosition = this.newSpawnPosition(snack); + if(spawnPosition == null) { + snack.remove(); + return; + } + snack.teleport(spawnPosition); + this.snacks.add(snack); + } + private void generateNewBomb() { + Entity bomb = new Entity(EntityType.FALLING_BLOCK); + FallingBlockMeta meta = (FallingBlockMeta) bomb.getEntityMeta(); + meta.setBlock(Block.TNT); + bomb.setInstance(this); + Pos spawnPosition = this.newSpawnPosition(bomb, 2); + if(spawnPosition == null) { + bomb.remove(); + return; + } + bomb.teleport(spawnPosition); + this.bombs.add(bomb); + } + + @Nullable + private Pos newSpawnPosition(Entity entity) { + return this.newSpawnPosition(entity, 0); + } + + @Nullable + private Pos newSpawnPosition(Entity entity, double border) { + Pos spawnPosition; + int counter = 0; + boolean isInRadius, collides; do { if(counter > 200) { - snack.remove(); - return; + return null; } int x = this.rnd.nextInt(-this.radius+2, this.radius-2); int z = this.rnd.nextInt(-this.radius+2, this.radius-2); @@ -135,19 +181,20 @@ class TurtleGame extends StatelessGame { Pos checkPosition = spawnPosition; isInRadius = checkPosition.distance(0, 1, 0) < this.radius-3; collides = this.getEntities().stream() - .filter(entity -> !entity.equals(snack)) - .anyMatch(entity -> snack.getBoundingBox().intersectBox(entity.getPosition().sub(checkPosition), entity.getBoundingBox())); + .filter(e -> !e.equals(entity)) + .anyMatch(e -> entity.getBoundingBox().growSymmetrically(border, border, border).intersectBox(e.getPosition().sub(checkPosition), e.getBoundingBox())); counter++; } while (!isInRadius || collides); - - snack.teleport(spawnPosition); - this.snacks.add(snack); + return spawnPosition; } @Override protected void onStart() { - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 10; i++) { this.generateNewSnack(); } + for (int i = 0; i < 5; i++) { + this.generateNewBomb(); + } } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index b47f82a..ffbe591 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -4,7 +4,6 @@ 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.game.stateless.config.common.NumericOption; import eu.mhsl.minenet.minigames.instance.room.Room; import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; @@ -27,13 +26,12 @@ public class TurtleGameFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new BoolOption("firstPerson", Material.SPYGLASS, TranslatedComponent.byId("game_TurtleGame#firstPerson"))) .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)); } @Override public Game manufacture(Room parent, Map> configuration) throws Exception { - return new TurtleGame(configuration.get("firstPerson").getAsBoolean(), configuration.get("radius").getAsInt()); + return new TurtleGame(configuration.get("radius").getAsInt()); } @Override From 8bd0ab1974d9a270c5c8fb8c2cb25180ddae0a09 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 13:39:57 +0200 Subject: [PATCH 08/38] fixed error after game ends --- .../types/turtleGame/TurtleGame.java | 22 +++++++++++++------ .../types/turtleGame/TurtleGameFactory.java | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 3b45fb7..1272b43 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -88,7 +88,7 @@ class TurtleGame extends StatelessGame { turtle.kill(); this.bombs.remove(bomb); bomb.remove(); - this.generateNewBomb(); + this.generateNewBomb(2); this.getScore().insertResult(p, this.scoreMap.get(p)); this.speed += 0.5; }); @@ -133,6 +133,12 @@ class TurtleGame extends StatelessGame { return new Pos(x, 1, z).withLookAt(new Pos(0, 0, 0)); } + private void generateNewSnack(int count) { + for (int i = 0; i < count; i++) { + this.generateNewSnack(); + } + } + private void generateNewSnack() { Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); @@ -147,6 +153,12 @@ class TurtleGame extends StatelessGame { this.snacks.add(snack); } + private void generateNewBomb(int count) { + for (int i = 0; i < count; i++) { + this.generateNewBomb(); + } + } + private void generateNewBomb() { Entity bomb = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) bomb.getEntityMeta(); @@ -190,11 +202,7 @@ class TurtleGame extends StatelessGame { @Override protected void onStart() { - for (int i = 0; i < 10; i++) { - this.generateNewSnack(); - } - for (int i = 0; i < 5; i++) { - this.generateNewBomb(); - } + this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); + this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index ffbe591..1913bb0 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -31,7 +31,7 @@ public class TurtleGameFactory implements GameFactory { @Override public Game manufacture(Room parent, Map> configuration) throws Exception { - return new TurtleGame(configuration.get("radius").getAsInt()); + return new TurtleGame(configuration.get("radius").getAsInt()).setParent(parent); } @Override From abf907af24a93677463413daf8ff2d3d99e4a766 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 13:49:49 +0200 Subject: [PATCH 09/38] added todos --- .../instance/game/stateless/types/turtleGame/TurtleGame.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 1272b43..34ed183 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -20,6 +20,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; +// TODO: Sound Effects, Partikel und Info für Score + class TurtleGame extends StatelessGame { private final int radius; private final Map turtlePlayerMap = new WeakHashMap<>(); @@ -27,6 +29,7 @@ class TurtleGame extends StatelessGame { private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); private double speed = 2; + private double maxSpeed = 10; public TurtleGame(int radius) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); @@ -90,6 +93,7 @@ class TurtleGame extends StatelessGame { bomb.remove(); this.generateNewBomb(2); this.getScore().insertResult(p, this.scoreMap.get(p)); + // TODO: Speed in Einstellungen anpassbar machen und je nach Spieleranzahl erhöhen this.speed += 0.5; }); } From 382d85060543151e2b5eeb5ac6b6168683ced542 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 14:03:44 +0200 Subject: [PATCH 10/38] added better speed mechanic --- .../game/stateless/types/turtleGame/TurtleGame.java | 11 ++++++----- .../stateless/types/turtleGame/TurtleGameFactory.java | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 34ed183..0b7f781 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -28,12 +28,14 @@ class TurtleGame extends StatelessGame { private final Map scoreMap = new WeakHashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); - private double speed = 2; - private double maxSpeed = 10; + private double speed; + private final double maxSpeedIncrease; - public TurtleGame(int radius) { + public TurtleGame(int radius, int startSpeed) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.radius = radius; + this.speed = startSpeed; + this.maxSpeedIncrease = (double) this.radius / 4; this.eventNode() .addListener(PlayerTickEvent.class, this::onPlayerTick); @@ -93,8 +95,7 @@ class TurtleGame extends StatelessGame { bomb.remove(); this.generateNewBomb(2); this.getScore().insertResult(p, this.scoreMap.get(p)); - // TODO: Speed in Einstellungen anpassbar machen und je nach Spieleranzahl erhöhen - this.speed += 0.5; + this.speed += this.maxSpeedIncrease / this.getPlayers().size(); }); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index 1913bb0..f5194f0 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -26,12 +26,13 @@ public class TurtleGameFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)); + .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)) + .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6)); } @Override public Game manufacture(Room parent, Map> configuration) throws Exception { - return new TurtleGame(configuration.get("radius").getAsInt()).setParent(parent); + return new TurtleGame(configuration.get("radius").getAsInt(), configuration.get("startSpeed").getAsInt()).setParent(parent); } @Override From 2a6f2f2a44e8834afbf46794aa7a42fcc7d1cd9f Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 15:39:19 +0200 Subject: [PATCH 11/38] added particle effects and sounds --- .../types/turtleGame/TurtleGame.java | 74 ++++++++++++------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 0b7f781..e14476b 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -3,7 +3,7 @@ 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.score.PointsWinScore; -import net.kyori.adventure.text.Component; +import net.kyori.adventure.sound.Sound; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; @@ -14,13 +14,16 @@ import net.minestom.server.event.player.PlayerTickEvent; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.minestom.server.network.packet.server.play.ParticlePacket; +import net.minestom.server.particle.Particle; +import net.minestom.server.sound.SoundEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; -// TODO: Sound Effects, Partikel und Info für Score +// TODO: Info für Score class TurtleGame extends StatelessGame { private final int radius; @@ -28,6 +31,7 @@ class TurtleGame extends StatelessGame { private final Map scoreMap = new WeakHashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); + private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); private double speed; private final double maxSpeedIncrease; @@ -72,38 +76,52 @@ class TurtleGame extends StatelessGame { Vec targetDirection = direction.withY(0).normalize().mul(this.speed); turtle.setVelocity(targetDirection); - List hitSnacks = this.snacks.stream() + this.snacks.stream() .filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) - .toList(); - List hitBombs = this.bombs.stream() + .toList() + .forEach(snack -> { + this.eat(p, snack); + this.generateNewSnack(); + if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 3 == 0) this.generateNewBomb(); + }); + this.bombs.stream() .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) - .toList(); - - hitSnacks.forEach(snack -> { - this.snacks.remove(snack); - snack.remove(); - this.generateNewSnack(); - this.scoreMap.put(p, this.scoreMap.get(p) + 1); - }); - hitBombs.forEach(bomb -> { - p.setGameMode(GameMode.SPECTATOR); - p.setFlying(true); - turtle.removePassenger(p); - turtle.remove(); - turtle.kill(); - this.bombs.remove(bomb); - bomb.remove(); - this.generateNewBomb(2); - this.getScore().insertResult(p, this.scoreMap.get(p)); - this.speed += this.maxSpeedIncrease / this.getPlayers().size(); - }); + .toList() + .forEach(bomb -> { + this.explode(p, bomb); + this.generateNewBomb(2); + this.speed += this.maxSpeedIncrease / this.getPlayers().size(); + }); } } + protected void eat(Player p, Entity snack) { + p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition()); + Material snackMaterial = this.snackBlock.registry().material(); + if(snackMaterial == null) snackMaterial = Material.DIRT; + p.sendPacket(new ParticlePacket(Particle.ITEM.withItem(ItemStack.of(snackMaterial)), p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); + this.snacks.remove(snack); + snack.remove(); + this.scoreMap.put(p, this.scoreMap.get(p) + 1); + } + + protected void explode(Player p, Entity bomb) { + EntityCreature turtle = this.turtlePlayerMap.get(p); + p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); + p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); + p.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); + p.setGameMode(GameMode.SPECTATOR); + p.setFlying(true); + turtle.removePassenger(p); + turtle.remove(); + turtle.kill(); + this.getScore().insertResult(p, this.scoreMap.get(p)); + this.bombs.remove(bomb); + bomb.remove(); + } + @Override protected boolean onPlayerJoin(Player p) { - p.getInventory().setItemStack(0, ItemStack.builder(Material.BARRIER).customName(Component.text("Reset Snack")).build()); - if(this.turtlePlayerMap.get(p) == null) { EntityCreature turtle = new EntityCreature(EntityType.TURTLE); this.turtlePlayerMap.put(p, turtle); @@ -147,7 +165,7 @@ class TurtleGame extends StatelessGame { private void generateNewSnack() { Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); - meta.setBlock(Block.SUNFLOWER.withProperty("half", "upper")); + meta.setBlock(this.snackBlock.withProperty("half", "upper")); snack.setInstance(this); Pos spawnPosition = this.newSpawnPosition(snack); if(spawnPosition == null) { From 2fac287e1e1118f792bd5f0cf9930b6dbff95182 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 16:11:10 +0200 Subject: [PATCH 12/38] added sidebar --- .../types/turtleGame/TurtleGame.java | 29 +++++++++++++++++-- .../types/turtleGame/TurtleGameFactory.java | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index e14476b..0d32701 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -4,6 +4,8 @@ import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.score.PointsWinScore; import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; @@ -16,6 +18,7 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.ParticlePacket; import net.minestom.server.particle.Particle; +import net.minestom.server.scoreboard.Sidebar; import net.minestom.server.sound.SoundEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,12 +26,11 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; -// TODO: Info für Score - class TurtleGame extends StatelessGame { private final int radius; private final Map turtlePlayerMap = new WeakHashMap<>(); private final Map scoreMap = new WeakHashMap<>(); + private final Map sidebarMap = new HashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); @@ -91,6 +93,7 @@ class TurtleGame extends StatelessGame { this.explode(p, bomb); this.generateNewBomb(2); this.speed += this.maxSpeedIncrease / this.getPlayers().size(); + this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineScore("1", (int) this.speed)); }); } } @@ -103,6 +106,7 @@ class TurtleGame extends StatelessGame { this.snacks.remove(snack); snack.remove(); this.scoreMap.put(p, this.scoreMap.get(p) + 1); + this.sidebarMap.get(p).updateLineScore("0", this.scoreMap.get(p)); } protected void explode(Player p, Entity bomb) { @@ -118,6 +122,7 @@ class TurtleGame extends StatelessGame { this.getScore().insertResult(p, this.scoreMap.get(p)); this.bombs.remove(bomb); bomb.remove(); + this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineScore("2", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())); } @Override @@ -127,6 +132,25 @@ class TurtleGame extends StatelessGame { this.turtlePlayerMap.put(p, turtle); } this.scoreMap.putIfAbsent(p, 0); + this.sidebarMap.putIfAbsent(p, new Sidebar(Component.text("Info:").color(NamedTextColor.GOLD))); + Sidebar sidebar = this.sidebarMap.get(p); + sidebar.createLine(new Sidebar.ScoreboardLine( + "0", + Component.text("Score: ").color(NamedTextColor.DARK_GREEN), + this.scoreMap.get(p) + )); + sidebar.createLine(new Sidebar.ScoreboardLine( + "1", + Component.text("Speed: ").color(NamedTextColor.DARK_BLUE), + (int) this.speed + )); + sidebar.createLine(new Sidebar.ScoreboardLine( + "2", + Component.text("Players: ").color(NamedTextColor.AQUA), + this.turtlePlayerMap.size() + )); + sidebar.addViewer(p); + this.sidebarMap.values().forEach(bar -> bar.updateLineScore("2", this.turtlePlayerMap.size())); EntityCreature turtle = this.turtlePlayerMap.get(p); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { @@ -143,6 +167,7 @@ class TurtleGame extends StatelessGame { protected void onPlayerLeave(Player p) { EntityCreature turtle = this.turtlePlayerMap.get(p); turtle.remove(); + this.sidebarMap.get(p).removeViewer(p); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index f5194f0..014260f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -27,7 +27,7 @@ public class TurtleGameFactory implements GameFactory { public ConfigManager configuration() { return new ConfigManager() .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)) - .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6)); + .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6, 8)); } @Override From 61aa7543be170dd727b60e499713faf59422105b Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 16:29:03 +0200 Subject: [PATCH 13/38] improved sidebar (ordered and colored) --- .../types/turtleGame/TurtleGame.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 0d32701..aaefdca 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -84,7 +84,7 @@ class TurtleGame extends StatelessGame { .forEach(snack -> { this.eat(p, snack); this.generateNewSnack(); - if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 3 == 0) this.generateNewBomb(); + if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) this.generateNewBomb(); }); this.bombs.stream() .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) @@ -93,7 +93,9 @@ class TurtleGame extends StatelessGame { this.explode(p, bomb); this.generateNewBomb(2); this.speed += this.maxSpeedIncrease / this.getPlayers().size(); - this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineScore("1", (int) this.speed)); + this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineContent("1", + Component.text("Speed: ").color(NamedTextColor.BLUE) + .append(Component.text(String.format("%3d", (int) this.speed)).color(NamedTextColor.RED)))); }); } } @@ -106,7 +108,9 @@ class TurtleGame extends StatelessGame { this.snacks.remove(snack); snack.remove(); this.scoreMap.put(p, this.scoreMap.get(p) + 1); - this.sidebarMap.get(p).updateLineScore("0", this.scoreMap.get(p)); + this.sidebarMap.get(p).updateLineContent("0", + Component.text("Score: ").color(NamedTextColor.GREEN) + .append(Component.text(String.format("%3d", this.scoreMap.get(p))).color(NamedTextColor.RED))); } protected void explode(Player p, Entity bomb) { @@ -122,7 +126,9 @@ class TurtleGame extends StatelessGame { this.getScore().insertResult(p, this.scoreMap.get(p)); this.bombs.remove(bomb); bomb.remove(); - this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineScore("2", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())); + this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineContent("2", + Component.text("Players: ").color(NamedTextColor.AQUA) + .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)))); } @Override @@ -136,21 +142,29 @@ class TurtleGame extends StatelessGame { Sidebar sidebar = this.sidebarMap.get(p); sidebar.createLine(new Sidebar.ScoreboardLine( "0", - Component.text("Score: ").color(NamedTextColor.DARK_GREEN), - this.scoreMap.get(p) + Component.text("Score: ").color(NamedTextColor.GREEN) + .append(Component.text(String.format("%3d", this.scoreMap.get(p))).color(NamedTextColor.RED)), + 3, + Sidebar.NumberFormat.blank() )); sidebar.createLine(new Sidebar.ScoreboardLine( "1", - Component.text("Speed: ").color(NamedTextColor.DARK_BLUE), - (int) this.speed + Component.text("Speed: ").color(NamedTextColor.BLUE) + .append(Component.text(String.format("%3d", (int) this.speed)).color(NamedTextColor.RED)), + 2, + Sidebar.NumberFormat.blank() )); sidebar.createLine(new Sidebar.ScoreboardLine( "2", - Component.text("Players: ").color(NamedTextColor.AQUA), - this.turtlePlayerMap.size() + Component.text("Players: ").color(NamedTextColor.AQUA) + .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)), + 1, + Sidebar.NumberFormat.blank() )); sidebar.addViewer(p); - this.sidebarMap.values().forEach(bar -> bar.updateLineScore("2", this.turtlePlayerMap.size())); + this.sidebarMap.values().forEach(bar -> bar.updateLineContent("2", + Component.text("Players: ").color(NamedTextColor.AQUA) + .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)))); EntityCreature turtle = this.turtlePlayerMap.get(p); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { From 75314748da3183db426c55e4759ce0efd8c78891 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 4 Oct 2025 18:32:27 +0200 Subject: [PATCH 14/38] removed sidebar, added names for items --- .../types/turtleGame/TurtleGame.java | 56 +++++-------------- .../types/turtleGame/TurtleGameFactory.java | 2 +- 2 files changed, 14 insertions(+), 44 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index aaefdca..015b920 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -6,6 +6,7 @@ import eu.mhsl.minenet.minigames.score.PointsWinScore; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; @@ -18,7 +19,6 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.network.packet.server.play.ParticlePacket; import net.minestom.server.particle.Particle; -import net.minestom.server.scoreboard.Sidebar; import net.minestom.server.sound.SoundEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,18 +30,15 @@ class TurtleGame extends StatelessGame { private final int radius; private final Map turtlePlayerMap = new WeakHashMap<>(); private final Map scoreMap = new WeakHashMap<>(); - private final Map sidebarMap = new HashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); private double speed; - private final double maxSpeedIncrease; public TurtleGame(int radius, int startSpeed) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.radius = radius; this.speed = startSpeed; - this.maxSpeedIncrease = (double) this.radius / 4; this.eventNode() .addListener(PlayerTickEvent.class, this::onPlayerTick); @@ -84,7 +81,10 @@ class TurtleGame extends StatelessGame { .forEach(snack -> { this.eat(p, snack); this.generateNewSnack(); - if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) this.generateNewBomb(); + if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) { + this.generateNewBomb(); + this.speed += 0.4; + } }); this.bombs.stream() .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) @@ -92,10 +92,7 @@ class TurtleGame extends StatelessGame { .forEach(bomb -> { this.explode(p, bomb); this.generateNewBomb(2); - this.speed += this.maxSpeedIncrease / this.getPlayers().size(); - this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineContent("1", - Component.text("Speed: ").color(NamedTextColor.BLUE) - .append(Component.text(String.format("%3d", (int) this.speed)).color(NamedTextColor.RED)))); + this.speed += 0.3; }); } } @@ -108,9 +105,7 @@ class TurtleGame extends StatelessGame { this.snacks.remove(snack); snack.remove(); this.scoreMap.put(p, this.scoreMap.get(p) + 1); - this.sidebarMap.get(p).updateLineContent("0", - Component.text("Score: ").color(NamedTextColor.GREEN) - .append(Component.text(String.format("%3d", this.scoreMap.get(p))).color(NamedTextColor.RED))); + p.setLevel(this.scoreMap.get(p)); } protected void explode(Player p, Entity bomb) { @@ -126,9 +121,6 @@ class TurtleGame extends StatelessGame { this.getScore().insertResult(p, this.scoreMap.get(p)); this.bombs.remove(bomb); bomb.remove(); - this.sidebarMap.values().forEach(sidebar -> sidebar.updateLineContent("2", - Component.text("Players: ").color(NamedTextColor.AQUA) - .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)))); } @Override @@ -138,33 +130,7 @@ class TurtleGame extends StatelessGame { this.turtlePlayerMap.put(p, turtle); } this.scoreMap.putIfAbsent(p, 0); - this.sidebarMap.putIfAbsent(p, new Sidebar(Component.text("Info:").color(NamedTextColor.GOLD))); - Sidebar sidebar = this.sidebarMap.get(p); - sidebar.createLine(new Sidebar.ScoreboardLine( - "0", - Component.text("Score: ").color(NamedTextColor.GREEN) - .append(Component.text(String.format("%3d", this.scoreMap.get(p))).color(NamedTextColor.RED)), - 3, - Sidebar.NumberFormat.blank() - )); - sidebar.createLine(new Sidebar.ScoreboardLine( - "1", - Component.text("Speed: ").color(NamedTextColor.BLUE) - .append(Component.text(String.format("%3d", (int) this.speed)).color(NamedTextColor.RED)), - 2, - Sidebar.NumberFormat.blank() - )); - sidebar.createLine(new Sidebar.ScoreboardLine( - "2", - Component.text("Players: ").color(NamedTextColor.AQUA) - .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)), - 1, - Sidebar.NumberFormat.blank() - )); - sidebar.addViewer(p); - this.sidebarMap.values().forEach(bar -> bar.updateLineContent("2", - Component.text("Players: ").color(NamedTextColor.AQUA) - .append(Component.text(String.format("%3d", (int) this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).count())).color(NamedTextColor.RED)))); + p.setLevel(this.scoreMap.get(p)); EntityCreature turtle = this.turtlePlayerMap.get(p); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { @@ -181,7 +147,6 @@ class TurtleGame extends StatelessGame { protected void onPlayerLeave(Player p) { EntityCreature turtle = this.turtlePlayerMap.get(p); turtle.remove(); - this.sidebarMap.get(p).removeViewer(p); } @Override @@ -205,6 +170,9 @@ class TurtleGame extends StatelessGame { Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); meta.setBlock(this.snackBlock.withProperty("half", "upper")); + meta.setCustomName(Component.text("Snack").color(NamedTextColor.WHITE)); + meta.setCustomNameVisible(true); + meta.setHasGlowingEffect(true); snack.setInstance(this); Pos spawnPosition = this.newSpawnPosition(snack); if(spawnPosition == null) { @@ -225,6 +193,8 @@ class TurtleGame extends StatelessGame { Entity bomb = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) bomb.getEntityMeta(); meta.setBlock(Block.TNT); + meta.setCustomName(Component.text("Bomb").color(NamedTextColor.RED).decorate(TextDecoration.BOLD)); + meta.setCustomNameVisible(true); bomb.setInstance(this); Pos spawnPosition = this.newSpawnPosition(bomb, 2); if(spawnPosition == null) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index 014260f..e91d47e 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -27,7 +27,7 @@ public class TurtleGameFactory implements GameFactory { public ConfigManager configuration() { return new ConfigManager() .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)) - .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6, 8)); + .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6, 8, 10)); } @Override From 39fb7f4956bd4a0c9c8fefd6f1cafe64b4378c1f Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 5 Oct 2025 00:13:56 +0200 Subject: [PATCH 15/38] added boost mechanic --- .../types/turtleGame/TurtleGame.java | 85 ++++++++------- .../types/turtleGame/gameObjects/Turtle.java | 103 ++++++++++++++++++ 2 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 015b920..39699ed 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -2,6 +2,7 @@ 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.gameObjects.Turtle; import eu.mhsl.minenet.minigames.score.PointsWinScore; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; @@ -9,10 +10,10 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; -import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.*; -import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.metadata.other.FallingBlockMeta; +import net.minestom.server.event.player.PlayerStartSneakingEvent; +import net.minestom.server.event.player.PlayerStopSneakingEvent; import net.minestom.server.event.player.PlayerTickEvent; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; @@ -28,20 +29,21 @@ import java.util.concurrent.CompletableFuture; class TurtleGame extends StatelessGame { private final int radius; - private final Map turtlePlayerMap = new WeakHashMap<>(); - private final Map scoreMap = new WeakHashMap<>(); + private final Map turtlePlayerMap = new WeakHashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); - private double speed; + private final double startSpeed; public TurtleGame(int radius, int startSpeed) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.radius = radius; - this.speed = startSpeed; + this.startSpeed = startSpeed; this.eventNode() - .addListener(PlayerTickEvent.class, this::onPlayerTick); + .addListener(PlayerTickEvent.class, this::onPlayerTick) + .addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak) + .addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak); } @Override @@ -65,38 +67,53 @@ class TurtleGame extends StatelessGame { } } + private void onPlayerStartSneak(@NotNull PlayerStartSneakingEvent event) { + Player p = event.getPlayer(); + this.turtlePlayerMap.get(p).boostSpeed(); + } + + private void onPlayerStopSneak(@NotNull PlayerStopSneakingEvent event) { + Player p = event.getPlayer(); + this.turtlePlayerMap.get(p).cancelBoost(); + } + protected void onPlayerTick(@NotNull PlayerTickEvent event) { Player p = event.getPlayer(); if(p.getGameMode() == GameMode.SPECTATOR) return; - EntityCreature turtle = this.turtlePlayerMap.get(p); - turtle.teleport(turtle.getPosition().withView(p.getPosition())); - Vec direction = p.getPosition().direction(); + Turtle turtle = this.turtlePlayerMap.get(p); + turtle.adaptView(); if(this.isRunning()) { - Vec targetDirection = direction.withY(0).normalize().mul(this.speed); - turtle.setVelocity(targetDirection); - + turtle.move(); this.snacks.stream() - .filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) + .filter(turtle::checkCollisionWithEntity) .toList() .forEach(snack -> { this.eat(p, snack); this.generateNewSnack(); - if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) { + if(this.turtlePlayerMap.get(p).getScore() % 5 == 0) { this.generateNewBomb(); - this.speed += 0.4; + this.addSpeed(0.4, p); } }); this.bombs.stream() - .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) + .filter(turtle::checkCollisionWithEntity) .toList() .forEach(bomb -> { this.explode(p, bomb); this.generateNewBomb(2); - this.speed += 0.3; + this.addGlobalSpeed(0.3); }); } } + protected void addSpeed(double amount, Player p) { + this.turtlePlayerMap.get(p).addSpeed(amount); + } + + protected void addGlobalSpeed(double amount) { + this.turtlePlayerMap.values().forEach(turtle -> turtle.addSpeed(amount)); + } + protected void eat(Player p, Entity snack) { p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition()); Material snackMaterial = this.snackBlock.registry().material(); @@ -104,48 +121,35 @@ class TurtleGame extends StatelessGame { p.sendPacket(new ParticlePacket(Particle.ITEM.withItem(ItemStack.of(snackMaterial)), p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); this.snacks.remove(snack); snack.remove(); - this.scoreMap.put(p, this.scoreMap.get(p) + 1); - p.setLevel(this.scoreMap.get(p)); + this.turtlePlayerMap.get(p).increaseScore(); } protected void explode(Player p, Entity bomb) { - EntityCreature turtle = this.turtlePlayerMap.get(p); + Turtle turtle = this.turtlePlayerMap.get(p); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); p.setGameMode(GameMode.SPECTATOR); p.setFlying(true); - turtle.removePassenger(p); - turtle.remove(); - turtle.kill(); - this.getScore().insertResult(p, this.scoreMap.get(p)); + turtle.destroy(); + this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore()); this.bombs.remove(bomb); bomb.remove(); } @Override protected boolean onPlayerJoin(Player p) { - if(this.turtlePlayerMap.get(p) == null) { - EntityCreature turtle = new EntityCreature(EntityType.TURTLE); - this.turtlePlayerMap.put(p, turtle); - } - this.scoreMap.putIfAbsent(p, 0); - p.setLevel(this.scoreMap.get(p)); - - EntityCreature turtle = this.turtlePlayerMap.get(p); - MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { - turtle.setInstance(this); - turtle.teleport(p.getPosition()); - turtle.addPassenger(p); - turtle.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); - }); + this.turtlePlayerMap.putIfAbsent(p, new Turtle(p, this.startSpeed)); + p.setLevel(this.turtlePlayerMap.get(p).getScore()); + Turtle turtle = this.turtlePlayerMap.get(p); + MinecraftServer.getSchedulerManager().scheduleNextTick(turtle::spawnTurtle); return super.onPlayerJoin(p); } @Override protected void onPlayerLeave(Player p) { - EntityCreature turtle = this.turtlePlayerMap.get(p); + Turtle turtle = this.turtlePlayerMap.get(p); turtle.remove(); } @@ -236,5 +240,6 @@ class TurtleGame extends StatelessGame { protected void onStart() { this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); + this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java new file mode 100644 index 0000000..e7f0434 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -0,0 +1,103 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.Player; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.timer.Task; +import net.minestom.server.timer.TaskSchedule; + +public class Turtle extends EntityCreature { + private final Player player; + private int score = 0; + private double speed; + private final double boostSpeedAmount = 4; + private float boostChargeLevel = 0f; + private Task boostTask; + private Task boostRefillTask; + + public Turtle(Player player, double speed) { + super(EntityType.TURTLE); + this.player = player; + this.speed = speed; + + this.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); + this.getAttribute(Attribute.MAX_HEALTH).setBaseValue(1); + this.player.setExp(this.boostChargeLevel); + } + + public void spawnTurtle() { + this.setInstance(this.player.getInstance()); + this.teleport(this.player.getPosition()); + this.addPassenger(this.player); + } + + public void startBoostRefill() { + this.boostRefillTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { + if(this.boostChargeLevel >= 1f) return; + this.boostChargeLevel = Math.min(1f, this.boostChargeLevel + 0.025f); + this.player.setExp(this.boostChargeLevel); + }, TaskSchedule.seconds(1), TaskSchedule.seconds(1)); + } + + public void destroy() { + this.removePassenger(this.player); + this.remove(); + this.kill(); + this.boostRefillTask.cancel(); + this.boostTask.cancel(); + } + + public void adaptView() { + this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch()))); + Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100); + this.lookAt(this.getPosition().add(lookingVector.asPosition())); + } + + public void move() { + this.adaptView(); + Vec direction = this.player.getPosition().direction(); + Vec movementVector = direction.withY(0).normalize().mul(this.speed); + this.setVelocity(movementVector); + } + + public boolean checkCollisionWithEntity(Entity other) { + return this.getBoundingBox().intersectBox(other.getPosition().sub(this.getPosition()), other.getBoundingBox()); + } + + public void boostSpeed() { + if(this.boostChargeLevel <= 0f) return; + this.speed += this.boostSpeedAmount; + this.boostTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { + if(this.boostChargeLevel <= 0f) { + this.cancelBoost(); + return; + } + this.boostChargeLevel = Math.max(0f, this.boostChargeLevel - 0.025f); + System.out.println(this.boostChargeLevel); + this.player.setExp(this.boostChargeLevel); + }, TaskSchedule.millis(30), TaskSchedule.millis(30)); + } + + public void cancelBoost() { + if(!this.boostTask.isAlive()) return; + this.boostTask.cancel(); + this.speed -= this.boostSpeedAmount; + } + + public void addSpeed(double amount) { + this.speed += amount; + } + + public int getScore() { + return this.score; + } + + public void increaseScore() { + this.score += 1; + this.player.setLevel(this.score); + } +} From dece9c13b73ee21a12d1aa1cbe9a73b95f95fb19 Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 5 Oct 2025 00:19:37 +0200 Subject: [PATCH 16/38] added translations --- src/main/resources/lang/locales.map.csv | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/lang/locales.map.csv b/src/main/resources/lang/locales.map.csv index 46b4bb2..606d08b 100644 --- a/src/main/resources/lang/locales.map.csv +++ b/src/main/resources/lang/locales.map.csv @@ -143,3 +143,8 @@ description;Stay on the high ground to win!;Bleibe solange wie möglich auf der ns:game_Fastbridge#;; name;Fastbridge;Fastbridge description;Speedbridge to the other platform. The first one there wins!;Baue dich so schnell wie möglich zur anderen Plattform. Wer zuerst dort ist, gewinnt! +;; +ns:game_TurtleGame#;; +name;Turtle Game;Turtle Game +description;Eat snacks and dodge bombs to get the highest score!;Esse Snacks und weiche Bomben aus, um den höchsten Score zu erreichen! +startSpeed;Start Speed;Startgeschwindigkeit From 84de61388e3c6c73b03522c5dc1ab41cd081fdcd Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 5 Oct 2025 18:24:46 +0200 Subject: [PATCH 17/38] improved speed mechanic and bomb spawning, added countdown for last player --- .../types/turtleGame/TurtleGame.java | 38 ++++++++++++++----- .../types/turtleGame/gameObjects/Turtle.java | 17 ++++++--- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 39699ed..a34ebab 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -125,18 +125,26 @@ class TurtleGame extends StatelessGame { } protected void explode(Player p, Entity bomb) { - Turtle turtle = this.turtlePlayerMap.get(p); + this.letPlayerLoose(p); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); - p.setGameMode(GameMode.SPECTATOR); - p.setFlying(true); - turtle.destroy(); - this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore()); + if(this.getLeftPlayers().size() == 1) this.setTimeLimit(10); this.bombs.remove(bomb); bomb.remove(); } + protected void letPlayerLoose(Player p) { + p.setGameMode(GameMode.SPECTATOR); + p.setFlying(true); + this.turtlePlayerMap.get(p).destroy(); + this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore()); + } + + protected List getLeftPlayers() { + return this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).toList(); + } + @Override protected boolean onPlayerJoin(Player p) { this.turtlePlayerMap.putIfAbsent(p, new Turtle(p, this.startSpeed)); @@ -200,7 +208,7 @@ class TurtleGame extends StatelessGame { meta.setCustomName(Component.text("Bomb").color(NamedTextColor.RED).decorate(TextDecoration.BOLD)); meta.setCustomNameVisible(true); bomb.setInstance(this); - Pos spawnPosition = this.newSpawnPosition(bomb, 2); + Pos spawnPosition = this.newSpawnPosition(bomb, false); if(spawnPosition == null) { bomb.remove(); return; @@ -211,11 +219,11 @@ class TurtleGame extends StatelessGame { @Nullable private Pos newSpawnPosition(Entity entity) { - return this.newSpawnPosition(entity, 0); + return this.newSpawnPosition(entity, true); } @Nullable - private Pos newSpawnPosition(Entity entity, double border) { + private Pos newSpawnPosition(Entity entity, boolean nearPlayers) { Pos spawnPosition; int counter = 0; boolean isInRadius, collides; @@ -227,10 +235,15 @@ class TurtleGame extends StatelessGame { int z = this.rnd.nextInt(-this.radius+2, this.radius-2); spawnPosition = new Pos(x, 1, z).add(0.5, 0, 0.5); Pos checkPosition = spawnPosition; - isInRadius = checkPosition.distance(0, 1, 0) < this.radius-3; + isInRadius = checkPosition.distance(0, 1, 0) < this.radius-2; collides = this.getEntities().stream() .filter(e -> !e.equals(entity)) - .anyMatch(e -> entity.getBoundingBox().growSymmetrically(border, border, border).intersectBox(e.getPosition().sub(checkPosition), e.getBoundingBox())); + .anyMatch(e -> entity.getBoundingBox().intersectBox(e.getPosition().sub(checkPosition), e.getBoundingBox())); + if(!collides && !nearPlayers) collides = this.turtlePlayerMap.values().stream() + .filter(turtle -> !turtle.equals(entity)) + .anyMatch(turtle -> entity.getBoundingBox() + .growSymmetrically(turtle.getBombBorder(), 1, turtle.getBombBorder()) + .intersectBox(turtle.getPosition().sub(checkPosition), turtle.getBoundingBox())); counter++; } while (!isInRadius || collides); return spawnPosition; @@ -242,4 +255,9 @@ class TurtleGame extends StatelessGame { this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill); } + + @Override + protected void onStop() { + this.getLeftPlayers().forEach(this::letPlayerLoose); + } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java index e7f0434..7c75ef1 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -14,7 +14,7 @@ public class Turtle extends EntityCreature { private final Player player; private int score = 0; private double speed; - private final double boostSpeedAmount = 4; + private double boostSpeedMultiplier = 1; private float boostChargeLevel = 0f; private Task boostTask; private Task boostRefillTask; @@ -47,8 +47,8 @@ public class Turtle extends EntityCreature { this.removePassenger(this.player); this.remove(); this.kill(); - this.boostRefillTask.cancel(); - this.boostTask.cancel(); + if(this.boostRefillTask.isAlive()) this.boostRefillTask.cancel(); + if(this.boostTask.isAlive()) this.boostTask.cancel(); } public void adaptView() { @@ -60,7 +60,7 @@ public class Turtle extends EntityCreature { public void move() { this.adaptView(); Vec direction = this.player.getPosition().direction(); - Vec movementVector = direction.withY(0).normalize().mul(this.speed); + Vec movementVector = direction.withY(0).normalize().mul(this.speed * this.boostSpeedMultiplier); this.setVelocity(movementVector); } @@ -70,7 +70,7 @@ public class Turtle extends EntityCreature { public void boostSpeed() { if(this.boostChargeLevel <= 0f) return; - this.speed += this.boostSpeedAmount; + this.boostSpeedMultiplier = 3.5; this.boostTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { if(this.boostChargeLevel <= 0f) { this.cancelBoost(); @@ -85,7 +85,7 @@ public class Turtle extends EntityCreature { public void cancelBoost() { if(!this.boostTask.isAlive()) return; this.boostTask.cancel(); - this.speed -= this.boostSpeedAmount; + this.boostSpeedMultiplier = 1; } public void addSpeed(double amount) { @@ -96,6 +96,11 @@ public class Turtle extends EntityCreature { return this.score; } + public double getBombBorder() { + // 1 bei speed 2; 2 bei speed 4; 4 bei speed 8 + return Math.clamp((this.speed * this.boostSpeedMultiplier) / 2, 1.5, 4); + } + public void increaseScore() { this.score += 1; this.player.setLevel(this.score); From ec76dd5c85c5cc463e94f53c63de5d3a1830f615 Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 6 Oct 2025 17:31:04 +0200 Subject: [PATCH 18/38] added boost charge when eating snacks --- .../game/stateless/types/turtleGame/TurtleGame.java | 2 +- .../stateless/types/turtleGame/gameObjects/Turtle.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index a34ebab..3146a3a 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -122,6 +122,7 @@ class TurtleGame extends StatelessGame { this.snacks.remove(snack); snack.remove(); this.turtlePlayerMap.get(p).increaseScore(); + this.turtlePlayerMap.get(p).increaseBoostChargeLevel(0.02f); } protected void explode(Player p, Entity bomb) { @@ -184,7 +185,6 @@ class TurtleGame extends StatelessGame { meta.setBlock(this.snackBlock.withProperty("half", "upper")); meta.setCustomName(Component.text("Snack").color(NamedTextColor.WHITE)); meta.setCustomNameVisible(true); - meta.setHasGlowingEffect(true); snack.setInstance(this); Pos spawnPosition = this.newSpawnPosition(snack); if(spawnPosition == null) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java index 7c75ef1..2f9328b 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -38,8 +38,7 @@ public class Turtle extends EntityCreature { public void startBoostRefill() { this.boostRefillTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { if(this.boostChargeLevel >= 1f) return; - this.boostChargeLevel = Math.min(1f, this.boostChargeLevel + 0.025f); - this.player.setExp(this.boostChargeLevel); + this.increaseBoostChargeLevel(0.02f); }, TaskSchedule.seconds(1), TaskSchedule.seconds(1)); } @@ -88,6 +87,11 @@ public class Turtle extends EntityCreature { this.boostSpeedMultiplier = 1; } + public void increaseBoostChargeLevel(float amount) { + this.boostChargeLevel = Math.min(1f, this.boostChargeLevel + amount); + this.player.setExp(this.boostChargeLevel); + } + public void addSpeed(double amount) { this.speed += amount; } From d5910b4b545de6d566bbb94b87c36b19f825c4d0 Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 6 Oct 2025 17:40:08 +0200 Subject: [PATCH 19/38] renamed methods --- .../game/stateless/types/turtleGame/TurtleGame.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 3146a3a..94dafbf 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -100,7 +100,7 @@ class TurtleGame extends StatelessGame { .toList() .forEach(bomb -> { this.explode(p, bomb); - this.generateNewBomb(2); + this.generateNewBombs(2); this.addGlobalSpeed(0.3); }); } @@ -173,7 +173,7 @@ class TurtleGame extends StatelessGame { return new Pos(x, 1, z).withLookAt(new Pos(0, 0, 0)); } - private void generateNewSnack(int count) { + private void generateNewSnacks(int count) { for (int i = 0; i < count; i++) { this.generateNewSnack(); } @@ -195,7 +195,7 @@ class TurtleGame extends StatelessGame { this.snacks.add(snack); } - private void generateNewBomb(int count) { + private void generateNewBombs(int count) { for (int i = 0; i < count; i++) { this.generateNewBomb(); } @@ -251,8 +251,8 @@ class TurtleGame extends StatelessGame { @Override protected void onStart() { - this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); - this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); + this.generateNewSnacks((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); + this.generateNewBombs((int) Math.ceil(this.snacks.size() * 0.5)); this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill); } From 097438886c7b4ee54ea4dbfefb22ca5c318c4cad Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 11 Oct 2025 12:22:50 +0200 Subject: [PATCH 20/38] added stickfight start and working length option --- .../types/stickfight/StickFightFactory.java | 4 ++-- .../types/stickfight/Stickfight.java | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/StickFightFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/StickFightFactory.java index f3be090..5fc1b12 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/StickFightFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/StickFightFactory.java @@ -28,7 +28,7 @@ public class StickFightFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 5, 7, 9, 11)); + .addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 7, 10, 13, 16, 19)); } @Override @@ -40,7 +40,7 @@ public class StickFightFactory implements GameFactory { @Override public Game manufacture(Room parent, Map> configuration) { - return new Stickfight().setParent(parent); + return new Stickfight(configuration.get("length").getAsInt()).setParent(parent); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java index 26709ec..5afb4c6 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java @@ -18,12 +18,14 @@ import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; public class Stickfight extends StatelessGame { - private final double radius = 20; + private final double radius; private final WeakHashMap spawnPoints = new WeakHashMap<>(); private final Map scoreMap = new WeakHashMap<>(); + private boolean countdownStarted = false; - public Stickfight() { + public Stickfight(int length) { super(Dimension.OVERWORLD.key, "Stickfight", new LowestPointsWinScore()); + this.radius = length; eventNode().addChild( CombatFeatures.empty() @@ -34,6 +36,7 @@ public class Stickfight extends StatelessGame { ); eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> { + if(isBeforeBeginning) finalAttackEvent.setCancelled(true); finalAttackEvent.setBaseDamage(0); ((Player) finalAttackEvent.getTarget()).setHealth(20); }); @@ -43,14 +46,26 @@ public class Stickfight extends StatelessGame { @Override protected void onLoad(@NotNull CompletableFuture callback) { - setBlock(0, 50, 0, Block.DIAMOND_BLOCK); + this.replaceCircle(Block.SANDSTONE); + } + + private void replaceCircle(Block block) { + int radius = 8; + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + Pos blockPosition = this.getSpawn().add(x, -1, z); + if(blockPosition.distance(this.getSpawn().sub(0, 1, 0)) <= radius) this.setBlock(blockPosition, block); + } + } } @Override protected void start() { List players = getPlayers().stream().toList(); int numPlayers = players.size(); + this.countdownStarted = true; + this.replaceCircle(Block.AIR); for (int i = 0; i < numPlayers; i++) { double angle = (2 * Math.PI / numPlayers) * i; int spawnX = (int) (radius * Math.cos(angle)); @@ -87,7 +102,8 @@ public class Stickfight extends StatelessGame { protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { Player player = playerMoveEvent.getPlayer(); if(!spawnPoints.containsKey(player)) { - playerMoveEvent.setCancelled(true); + if(playerMoveEvent.getNewPosition().y() < 45) player.teleport(this.getSpawn()); + if(this.countdownStarted) playerMoveEvent.setCancelled(true); return; } From c4aaa7acf926268c6af5c167022b736fe6af7ac4 Mon Sep 17 00:00:00 2001 From: lars Date: Sat, 11 Oct 2025 15:17:23 +0200 Subject: [PATCH 21/38] added flowers and grass to HeightTerrainGenerator --- .../mhsl/minenet/minigames/world/BlockPallet.java | 2 +- .../generator/terrain/HeightTerrainGenerator.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/world/BlockPallet.java b/src/main/java/eu/mhsl/minenet/minigames/world/BlockPallet.java index 483d893..9dde82b 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/world/BlockPallet.java +++ b/src/main/java/eu/mhsl/minenet/minigames/world/BlockPallet.java @@ -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}), + FLOWER(new Block[] {Block.ORANGE_TULIP, Block.PINK_TULIP, Block.RED_TULIP, Block.WHITE_TULIP, Block.DANDELION, Block.POPPY, Block.CORNFLOWER}), 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 list; diff --git a/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/HeightTerrainGenerator.java b/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/HeightTerrainGenerator.java index 362d3fe..a3a1c08 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/HeightTerrainGenerator.java +++ b/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/HeightTerrainGenerator.java @@ -55,7 +55,19 @@ public class HeightTerrainGenerator extends BaseGenerator { synchronized (batches) { double batchNoise = batches.getNoise(bottomPoint.x(), bottomPoint.z()); - unit.modifier().fill(bottomPoint, bottomPoint.add(1, heightModifier, 1), batchNoise < 0.9 ? batchNoise > 0 ? Block.GRASS_BLOCK : Block.SOUL_SAND : Block.STONE); + Block block = batchNoise < 0.9 ? batchNoise > -0.2 ? Block.GRASS_BLOCK : Block.SOUL_SAND : Block.STONE; + unit.modifier().fill(bottomPoint, bottomPoint.add(1, heightModifier, 1), block); + if(rnd.nextInt(0, 5) < 1 && block == Block.GRASS_BLOCK) { + int randomInt = rnd.nextInt(0, 6); + if(randomInt > 1) { + unit.modifier().setBlock(bottomPoint.add(0, heightModifier, 0), Block.SHORT_GRASS); + } else if(randomInt > 0) { + unit.modifier().setBlock(bottomPoint.add(0, heightModifier, 0), BlockPallet.FLOWER.rnd()); + } else { + unit.modifier().setBlock(bottomPoint.add(0, heightModifier, 0), Block.TALL_GRASS); + unit.modifier().setBlock(bottomPoint.add(0, heightModifier+1, 0), Block.TALL_GRASS.withProperty("half", "upper")); + } + } if(calculateSeaLevel != null) { Point absoluteHeight = bottomPoint.add(0, heightModifier, 0); From 5bb07596a176e355e95158bb70d85835273e7b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 00:42:11 +0200 Subject: [PATCH 22/38] added SpaceSnake game and related assets --- .../minigames/instance/game/GameList.java | 4 +- .../types/spaceSnake/SpaceSnake.java | 181 ++++++++++++++++++ .../types/spaceSnake/SpaceSnakeFactory.java | 41 ++++ src/main/resources/lang/locales.map.csv | 6 +- 4 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnakeFactory.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java index 150a603..c7c3651 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java @@ -13,6 +13,7 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge.Fastbr 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.spaceSnake.SpaceSnakeFactory; 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; @@ -40,7 +41,8 @@ public enum GameList { SUMO(new SumoFactory(), GameType.PVP), HIGHGROUND(new HighGroundFactory(), GameType.PVP), FASTBRIDGE(new FastbridgeFactory(), GameType.OTHER), - BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER); + BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER), + SNAKE3D(new SpaceSnakeFactory(), GameType.PVP); private final GameFactory factory; private final GameType type; diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java new file mode 100644 index 0000000..2411903 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -0,0 +1,181 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.spaceSnake; + +import eu.mhsl.minenet.minigames.instance.Dimension; +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import eu.mhsl.minenet.minigames.score.PointsWinScore; +import io.github.togar2.pvp.events.FinalAttackEvent; +import io.github.togar2.pvp.events.PrepareAttackEvent; +import io.github.togar2.pvp.feature.CombatFeatures; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.*; +import net.minestom.server.entity.metadata.display.BlockDisplayMeta; +import net.minestom.server.event.entity.EntityTickEvent; +import net.minestom.server.event.player.PlayerBlockPlaceEvent; +import net.minestom.server.event.player.PlayerMoveEvent; +import net.minestom.server.instance.WorldBorder; +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.BlockFace; +import net.minestom.server.inventory.PlayerInventory; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.sound.SoundEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class SpaceSnake extends StatelessGame { + record PlayState(AtomicInteger length, Queue blocks, Material blockType, Pos spawn) { + public void cutToLength(Consumer removed) { + while (this.blocks.size() > this.length.get()) { + removed.accept(this.blocks.poll()); + } + } + } + + private final Map playerBlocks = new WeakHashMap<>(); + private int mapSize; + private final Supplier posInBoundsW = () -> this.rnd.nextInt(-this.mapSize /2, this.mapSize /2); + private final Supplier posInBoundsH = () -> this.rnd.nextInt(-32, 100); + + public SpaceSnake(int mapSize, int powerUpCount) { + super(Dimension.THE_END.key, "spaceSnake", new PointsWinScore()); + this.mapSize = mapSize; + this.setWorldBorder(new WorldBorder(this.mapSize, 0, 0, 0, 0)); + + for (int i = 0; i < powerUpCount; i++) { + this.spawnPowerUp(); + } + + 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 onStart() { + this.getPlayers().forEach(player -> { + player.setGameMode(GameMode.SURVIVAL); + this.updateInv(player); + player.setHeldItemSlot((byte) 1); + }); + } + + @Override + protected boolean onPlayerJoin(Player p) { + Pos spawn = new Pos(this.posInBoundsW.get(), 50, this.posInBoundsW.get()); + PlayState state = new PlayState( + new AtomicInteger(3), + new ArrayDeque<>(List.of(spawn)), + this.getRandomBlock(), + spawn + ); + this.playerBlocks.put(p, state); + this.setBlock(spawn, state.blockType.block()); + MinecraftServer.getSchedulerManager().scheduleNextTick( + () -> p.teleport(this.getSaveSpawn(spawn)) + ); + return super.onPlayerJoin(p); + } + + @Override + protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { + PlayState state = this.playerBlocks.get(playerMoveEvent.getPlayer()); + if(this.isBeforeBeginning) { + boolean falling = state.blocks.stream().anyMatch(pos -> pos.y() > playerMoveEvent.getNewPosition().y()); + if(falling) playerMoveEvent.getPlayer().teleport(this.getSaveSpawn(state.spawn)); + return; + } + + if(playerMoveEvent.getNewPosition().y() < -64) { + this.getScore().insertResult(playerMoveEvent.getPlayer(), state.length.get()); + playerMoveEvent.getPlayer().teleport(this.getSpawn()); + playerMoveEvent.getPlayer().setGameMode(GameMode.SPECTATOR); + } + } + + @Override + protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent playerBlockPlaceEvent) { + if(this.isBeforeBeginning) { + playerBlockPlaceEvent.setCancelled(true); + return; + } + + PlayState state = this.playerBlocks.get(playerBlockPlaceEvent.getPlayer()); + state.blocks.add(playerBlockPlaceEvent.getBlockPosition().asVec().asPosition()); + state.cutToLength(pos -> this.setBlock(pos, Block.AIR)); + + MinecraftServer.getSchedulerManager().scheduleNextTick(() -> this.updateInv(playerBlockPlaceEvent.getPlayer())); + playerBlockPlaceEvent.getPlayer().setLevel(state.length.get()); + } + + private Pos getSaveSpawn(Pos blockPos) { + return blockPos.add(0.5).withY((y ) -> y + 2); + } + + private void updateInv(Player player) { + PlayerInventory inventory = player.getInventory(); + inventory.clear(); + inventory.addItemStack(ItemStack.of(Material.STICK, 1).with(builder -> builder.glowing(true))); + inventory.addItemStack(ItemStack.of(this.playerBlocks.get(player).blockType, 64)); + } + + private Material getRandomBlock() { + List blocks = Material.values().stream() + .filter(Material::isBlock) + .filter(material -> material.registry().block() != null) + .filter(material -> material.block().isSolid()) + .filter(material -> Arrays.stream(BlockFace.values()) + .allMatch(face -> material.block().registry().collisionShape().isFaceFull(face)) + ) + .toList(); + return blocks.get(this.rnd.nextInt(blocks.size())); + } + + private void spawnPowerUp() { + Pos spawnPos = new Pos(this.posInBoundsW.get(), this.posInBoundsH.get(), this.posInBoundsW.get()); + Entity display = new Entity(EntityType.BLOCK_DISPLAY); + ((BlockDisplayMeta) display.getEntityMeta()).setBlockState(Block.DIAMOND_BLOCK); + display.setGlowing(true); + display.setNoGravity(true); + display.setInstance(this, spawnPos); + + display.eventNode().addListener(EntityTickEvent.class, onTick -> { + Player player = this.getPlayers().stream() + .filter(p -> !this.getScore().hasResult(p)) + .filter(p -> p.getDistance(onTick.getEntity()) < 2.5) + .findAny() + .orElse(null); + if(player == null) return; + + this.spawnPowerUp(); + display.remove(); + this.onPowerup(player); + }); + } + + private void onPowerup(Player player) { + PlayState state = this.playerBlocks.get(player); + state.length.incrementAndGet(); + player.setLevel(player.getLevel() + 1); + player.playSound(Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.PLAYER, 1f, 1f)); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnakeFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnakeFactory.java new file mode 100644 index 0000000..97a17a5 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnakeFactory.java @@ -0,0 +1,41 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.spaceSnake; + +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 SpaceSnakeFactory implements GameFactory { + @Override + public TranslatedComponent name() { + return TranslatedComponent.byId("game_SpaceSnake#name"); + } + + @Override + public ConfigManager configuration() { + return new ConfigManager() + .addOption(new NumericOption("width", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#width"), 20, 30, 40, 50, 60, 70, 80)) + .addOption(new NumericOption("powerUpCount", Material.DIAMOND, TranslatedComponent.byId("game_SpaceSnake#powerUpCount"), 50, 100, 200, 300)); + } + + @Override + public Material symbol() { + return Material.GREEN_CONCRETE_POWDER; + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_SpaceSnake#description"); + } + + @Override + public Game manufacture(Room parent, Map> configuration) throws Exception { + return new SpaceSnake(configuration.get("width").getAsInt(), configuration.get("powerUpCount").getAsInt()).setParent(parent); + } +} diff --git a/src/main/resources/lang/locales.map.csv b/src/main/resources/lang/locales.map.csv index d2167ae..749dbe8 100644 --- a/src/main/resources/lang/locales.map.csv +++ b/src/main/resources/lang/locales.map.csv @@ -147,4 +147,8 @@ description;Speedbridge to the other platform. The first one there wins!;Baue di ns:game_BlockBreakRace#;; name;Block Break Race;Blockbruch-Rennen description;Dig down through the tubes using the right tools. The first player to reach the bottom wins!;Grabe dich durch die Röhren nach unten und verwende dabei das richtige Werkzeug. Wer zuerst unten ankommt, gewinnt! -;; \ No newline at end of file +;; +ns:game_SpaceSnake#;; +name;Space Snake;Weltraum-Snake +description;Collect diamonds while extending your snake bridge through space and fight other players. The player with the longest bridge wins!;Sammle Diamanten, während du deine Schlangenbrücke durchs All erweiterst und gegen andere Spieler kämpfst. Der Spieler mit den der Längsten Brücke gewinnt! +powerUpCount;Number of diamonds in the arena;Anzahl der Diamanten in der Arena From db78ff33ce9407cd0a7ae5f689baf607099b3085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 00:46:34 +0200 Subject: [PATCH 23/38] fixed typo in game name: renamed SNAKE3D to SPACESNAKE --- .../java/eu/mhsl/minenet/minigames/instance/game/GameList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java index c7c3651..98943df 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java @@ -42,7 +42,7 @@ public enum GameList { HIGHGROUND(new HighGroundFactory(), GameType.PVP), FASTBRIDGE(new FastbridgeFactory(), GameType.OTHER), BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER), - SNAKE3D(new SpaceSnakeFactory(), GameType.PVP); + SPACESNAKE(new SpaceSnakeFactory(), GameType.PVP); private final GameFactory factory; private final GameType type; From a8a15a1c7c2d3fbbac7907ba43f5d9bad9bd8176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 00:51:08 +0200 Subject: [PATCH 24/38] adjusted SpaceSnake spawn position and random bounds --- .../game/stateless/types/spaceSnake/SpaceSnake.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java index 2411903..09b4f50 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -39,8 +39,8 @@ public class SpaceSnake extends StatelessGame { private final Map playerBlocks = new WeakHashMap<>(); private int mapSize; - private final Supplier posInBoundsW = () -> this.rnd.nextInt(-this.mapSize /2, this.mapSize /2); - private final Supplier posInBoundsH = () -> this.rnd.nextInt(-32, 100); + private final Supplier posInBoundsW = () -> this.rnd.nextInt(-this.mapSize/2, this.mapSize/2); + private final Supplier posInBoundsH = () -> this.rnd.nextInt(-60, 300); public SpaceSnake(int mapSize, int powerUpCount) { super(Dimension.THE_END.key, "spaceSnake", new PointsWinScore()); @@ -81,7 +81,7 @@ public class SpaceSnake extends StatelessGame { @Override protected boolean onPlayerJoin(Player p) { - Pos spawn = new Pos(this.posInBoundsW.get(), 50, this.posInBoundsW.get()); + Pos spawn = new Pos(this.posInBoundsW.get(), -60, this.posInBoundsW.get()); PlayState state = new PlayState( new AtomicInteger(3), new ArrayDeque<>(List.of(spawn)), From 9596800889d5e8374561764c7ac8f3806a75b919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 00:51:50 +0200 Subject: [PATCH 25/38] fixed extra space in lambda expression in SpaceSnake spawn calculation --- .../instance/game/stateless/types/spaceSnake/SpaceSnake.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java index 09b4f50..904d147 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -128,7 +128,7 @@ public class SpaceSnake extends StatelessGame { } private Pos getSaveSpawn(Pos blockPos) { - return blockPos.add(0.5).withY((y ) -> y + 2); + return blockPos.add(0.5).withY((y) -> y + 2); } private void updateInv(Player player) { From bc3f5f58a45f7476168c0e2d6ed3e389315584a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 19:28:48 +0200 Subject: [PATCH 26/38] prevent diamond block as building block in SpaceSnake --- .../types/spaceSnake/SpaceSnake.java | 16 ++-------------- .../minenet/minigames/util/MaterialUtil.java | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java index 904d147..5f8af96 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -3,6 +3,7 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.spaceSnake; 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.util.MaterialUtil; import io.github.togar2.pvp.events.FinalAttackEvent; import io.github.togar2.pvp.events.PrepareAttackEvent; import io.github.togar2.pvp.feature.CombatFeatures; @@ -16,7 +17,6 @@ import net.minestom.server.event.player.PlayerBlockPlaceEvent; import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.instance.WorldBorder; import net.minestom.server.instance.block.Block; -import net.minestom.server.instance.block.BlockFace; import net.minestom.server.inventory.PlayerInventory; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; @@ -85,7 +85,7 @@ public class SpaceSnake extends StatelessGame { PlayState state = new PlayState( new AtomicInteger(3), new ArrayDeque<>(List.of(spawn)), - this.getRandomBlock(), + MaterialUtil.getRandomFullBlock(material -> !material.equals(Material.DIAMOND_BLOCK)), spawn ); this.playerBlocks.put(p, state); @@ -138,18 +138,6 @@ public class SpaceSnake extends StatelessGame { inventory.addItemStack(ItemStack.of(this.playerBlocks.get(player).blockType, 64)); } - private Material getRandomBlock() { - List blocks = Material.values().stream() - .filter(Material::isBlock) - .filter(material -> material.registry().block() != null) - .filter(material -> material.block().isSolid()) - .filter(material -> Arrays.stream(BlockFace.values()) - .allMatch(face -> material.block().registry().collisionShape().isFaceFull(face)) - ) - .toList(); - return blocks.get(this.rnd.nextInt(blocks.size())); - } - private void spawnPowerUp() { Pos spawnPos = new Pos(this.posInBoundsW.get(), this.posInBoundsH.get(), this.posInBoundsW.get()); Entity display = new Entity(EntityType.BLOCK_DISPLAY); diff --git a/src/main/java/eu/mhsl/minenet/minigames/util/MaterialUtil.java b/src/main/java/eu/mhsl/minenet/minigames/util/MaterialUtil.java index 6effaf9..3fbfa90 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/util/MaterialUtil.java +++ b/src/main/java/eu/mhsl/minenet/minigames/util/MaterialUtil.java @@ -1,9 +1,28 @@ package eu.mhsl.minenet.minigames.util; +import net.minestom.server.instance.block.BlockFace; import net.minestom.server.item.Material; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Predicate; + public class MaterialUtil { public static Material fromString(String name, Material def) { return Material.values().stream().filter(material -> material.name().equals(name)).findFirst().orElse(def); } + + public static Material getRandomFullBlock(Predicate filter) { + List blocks = Material.values().stream() + .filter(filter) + .filter(Material::isBlock) + .filter(material -> material.block().isSolid()) + .filter(material -> Arrays.stream(BlockFace.values()) + .allMatch(face -> material.block().registry().collisionShape().isFaceFull(face)) + ) + .toList(); + + return blocks.get(ThreadLocalRandom.current().nextInt(blocks.size())); + } } From 41028e3389502968616a7e00830310c1b9f6548e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 20:36:52 +0200 Subject: [PATCH 27/38] SpaceSnake displayBlock to Fallingblock powerup detection via boundingbox --- .../game/stateless/types/spaceSnake/SpaceSnake.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java index 5f8af96..2589e89 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -11,7 +11,7 @@ import net.kyori.adventure.sound.Sound; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.*; -import net.minestom.server.entity.metadata.display.BlockDisplayMeta; +import net.minestom.server.entity.metadata.other.FallingBlockMeta; import net.minestom.server.event.entity.EntityTickEvent; import net.minestom.server.event.player.PlayerBlockPlaceEvent; import net.minestom.server.event.player.PlayerMoveEvent; @@ -140,8 +140,8 @@ public class SpaceSnake extends StatelessGame { private void spawnPowerUp() { Pos spawnPos = new Pos(this.posInBoundsW.get(), this.posInBoundsH.get(), this.posInBoundsW.get()); - Entity display = new Entity(EntityType.BLOCK_DISPLAY); - ((BlockDisplayMeta) display.getEntityMeta()).setBlockState(Block.DIAMOND_BLOCK); + Entity display = new Entity(EntityType.FALLING_BLOCK); + ((FallingBlockMeta) display.getEntityMeta()).setBlock(Block.DIAMOND_BLOCK); display.setGlowing(true); display.setNoGravity(true); display.setInstance(this, spawnPos); @@ -149,7 +149,10 @@ public class SpaceSnake extends StatelessGame { display.eventNode().addListener(EntityTickEvent.class, onTick -> { Player player = this.getPlayers().stream() .filter(p -> !this.getScore().hasResult(p)) - .filter(p -> p.getDistance(onTick.getEntity()) < 2.5) + .filter(p -> p.getBoundingBox() + .grow(1, 1, 1) + .intersectBox(display.getPosition().sub(p.getPosition()), display.getBoundingBox()) + ) .findAny() .orElse(null); if(player == null) return; From 6076c0ca15bdd4f7f6b7ac548adf4b004f15f45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 20:54:26 +0200 Subject: [PATCH 28/38] Refactored player state management in SpaceSnake and implemented endgame handling --- .../types/spaceSnake/SpaceSnake.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java index 2589e89..6e2ab4e 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spaceSnake/SpaceSnake.java @@ -37,7 +37,7 @@ public class SpaceSnake extends StatelessGame { } } - private final Map playerBlocks = new WeakHashMap<>(); + private final Map playerStates = new WeakHashMap<>(); private int mapSize; private final Supplier posInBoundsW = () -> this.rnd.nextInt(-this.mapSize/2, this.mapSize/2); private final Supplier posInBoundsH = () -> this.rnd.nextInt(-60, 300); @@ -79,6 +79,11 @@ public class SpaceSnake extends StatelessGame { }); } + @Override + protected void onStop() { + this.getPlayers().forEach(player -> this.getScore().insertResult(player, this.playerStates.get(player).length.get())); + } + @Override protected boolean onPlayerJoin(Player p) { Pos spawn = new Pos(this.posInBoundsW.get(), -60, this.posInBoundsW.get()); @@ -88,7 +93,7 @@ public class SpaceSnake extends StatelessGame { MaterialUtil.getRandomFullBlock(material -> !material.equals(Material.DIAMOND_BLOCK)), spawn ); - this.playerBlocks.put(p, state); + this.playerStates.put(p, state); this.setBlock(spawn, state.blockType.block()); MinecraftServer.getSchedulerManager().scheduleNextTick( () -> p.teleport(this.getSaveSpawn(spawn)) @@ -98,7 +103,7 @@ public class SpaceSnake extends StatelessGame { @Override protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { - PlayState state = this.playerBlocks.get(playerMoveEvent.getPlayer()); + PlayState state = this.playerStates.get(playerMoveEvent.getPlayer()); if(this.isBeforeBeginning) { boolean falling = state.blocks.stream().anyMatch(pos -> pos.y() > playerMoveEvent.getNewPosition().y()); if(falling) playerMoveEvent.getPlayer().teleport(this.getSaveSpawn(state.spawn)); @@ -109,6 +114,11 @@ public class SpaceSnake extends StatelessGame { this.getScore().insertResult(playerMoveEvent.getPlayer(), state.length.get()); playerMoveEvent.getPlayer().teleport(this.getSpawn()); playerMoveEvent.getPlayer().setGameMode(GameMode.SPECTATOR); + long livingPlayers = this.getPlayers().stream() + .filter(p -> this.getScore().hasResult(p)) + .count(); + if(livingPlayers == 1) this.setTimeLimit(10); + if(livingPlayers == 0) this.stop(); } } @@ -119,7 +129,7 @@ public class SpaceSnake extends StatelessGame { return; } - PlayState state = this.playerBlocks.get(playerBlockPlaceEvent.getPlayer()); + PlayState state = this.playerStates.get(playerBlockPlaceEvent.getPlayer()); state.blocks.add(playerBlockPlaceEvent.getBlockPosition().asVec().asPosition()); state.cutToLength(pos -> this.setBlock(pos, Block.AIR)); @@ -135,7 +145,7 @@ public class SpaceSnake extends StatelessGame { PlayerInventory inventory = player.getInventory(); inventory.clear(); inventory.addItemStack(ItemStack.of(Material.STICK, 1).with(builder -> builder.glowing(true))); - inventory.addItemStack(ItemStack.of(this.playerBlocks.get(player).blockType, 64)); + inventory.addItemStack(ItemStack.of(this.playerStates.get(player).blockType, 64)); } private void spawnPowerUp() { @@ -164,7 +174,7 @@ public class SpaceSnake extends StatelessGame { } private void onPowerup(Player player) { - PlayState state = this.playerBlocks.get(player); + PlayState state = this.playerStates.get(player); state.length.incrementAndGet(); player.setLevel(player.getLevel() + 1); player.playSound(Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.PLAYER, 1f, 1f)); From 3dfff84c61a0db94d3f00515a97259415b02bd09 Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 20:55:30 +0200 Subject: [PATCH 29/38] changed left player detection to use hasResult of Score --- .../instance/game/stateless/types/turtleGame/TurtleGame.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 94dafbf..12936c4 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -143,7 +143,9 @@ class TurtleGame extends StatelessGame { } protected List getLeftPlayers() { - return this.turtlePlayerMap.keySet().stream().filter(player -> !player.isFlying()).toList(); + return this.turtlePlayerMap.keySet().stream() + .filter(player -> !this.getScore().hasResult(player)) + .toList(); } @Override From 1830307f4bf5b18be2d2af7769e271afa33ba40f Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:03:12 +0200 Subject: [PATCH 30/38] increased boost refill when eating flowers, changed speed options --- .../instance/game/stateless/types/turtleGame/TurtleGame.java | 2 +- .../game/stateless/types/turtleGame/TurtleGameFactory.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 12936c4..fdaa064 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -122,7 +122,7 @@ class TurtleGame extends StatelessGame { this.snacks.remove(snack); snack.remove(); this.turtlePlayerMap.get(p).increaseScore(); - this.turtlePlayerMap.get(p).increaseBoostChargeLevel(0.02f); + this.turtlePlayerMap.get(p).increaseBoostChargeLevel(0.04f); } protected void explode(Player p, Entity bomb) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java index e91d47e..1f66cf5 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGameFactory.java @@ -27,7 +27,7 @@ public class TurtleGameFactory implements GameFactory { public ConfigManager configuration() { return new ConfigManager() .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40)) - .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 4, 6, 8, 10)); + .addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 3, 4, 6, 8)); } @Override From c8bf5f9186b176ead94c0d008f8dbbcc37ef5acb Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:04:46 +0200 Subject: [PATCH 31/38] added turtle game to pve --- .../java/eu/mhsl/minenet/minigames/instance/game/GameList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java index ad3504f..33b9727 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java @@ -30,11 +30,11 @@ 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), + TURTLEGAME(new TurtleGameFactory(), GameType.PVE), ELYTRARACE(new ElytraRaceFactory(), GameType.PVP), SPLEEF(new SpleefFactory(), GameType.PVP), JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN), From eff5e36987fee3aaa0636b2d127c7221d13ed100 Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:26:02 +0200 Subject: [PATCH 32/38] solved some pr comments --- .../types/turtleGame/TurtleGame.java | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index fdaa064..47d7754 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -48,10 +48,6 @@ class TurtleGame extends StatelessGame { @Override protected void onLoad(@NotNull CompletableFuture callback) { - this.generatePlatform(); - } - - private void generatePlatform() { for(int x = -this.radius - 1; x <= this.radius + 1; x++) { for(int z = -this.radius - 1; z <= this.radius + 1; z++) { double distance = new Pos(x, 0, z).distance(new Pos(0, 0, 0)); @@ -116,8 +112,7 @@ class TurtleGame extends StatelessGame { protected void eat(Player p, Entity snack) { p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition()); - Material snackMaterial = this.snackBlock.registry().material(); - if(snackMaterial == null) snackMaterial = Material.DIRT; + Material snackMaterial = Objects.requireNonNull(this.snackBlock.registry().material()); p.sendPacket(new ParticlePacket(Particle.ITEM.withItem(ItemStack.of(snackMaterial)), p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); this.snacks.remove(snack); snack.remove(); @@ -184,16 +179,15 @@ class TurtleGame extends StatelessGame { private void generateNewSnack() { Entity snack = new Entity(EntityType.FALLING_BLOCK); FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta(); - meta.setBlock(this.snackBlock.withProperty("half", "upper")); - meta.setCustomName(Component.text("Snack").color(NamedTextColor.WHITE)); + meta.setBlock(this.snackBlock); + meta.setCustomName(Component.text("Snack")); meta.setCustomNameVisible(true); - snack.setInstance(this); Pos spawnPosition = this.newSpawnPosition(snack); if(spawnPosition == null) { snack.remove(); return; } - snack.teleport(spawnPosition); + snack.setInstance(this, spawnPosition); this.snacks.add(snack); } @@ -209,30 +203,25 @@ class TurtleGame extends StatelessGame { meta.setBlock(Block.TNT); meta.setCustomName(Component.text("Bomb").color(NamedTextColor.RED).decorate(TextDecoration.BOLD)); meta.setCustomNameVisible(true); - bomb.setInstance(this); Pos spawnPosition = this.newSpawnPosition(bomb, false); if(spawnPosition == null) { bomb.remove(); return; } - bomb.teleport(spawnPosition); + bomb.setInstance(this, spawnPosition); this.bombs.add(bomb); } - @Nullable - private Pos newSpawnPosition(Entity entity) { + private @Nullable Pos newSpawnPosition(Entity entity) { return this.newSpawnPosition(entity, true); } - @Nullable - private Pos newSpawnPosition(Entity entity, boolean nearPlayers) { + private @Nullable Pos newSpawnPosition(Entity entity, boolean nearPlayers) { Pos spawnPosition; int counter = 0; boolean isInRadius, collides; do { - if(counter > 200) { - return null; - } + if(counter > 200) return null; int x = this.rnd.nextInt(-this.radius+2, this.radius-2); int z = this.rnd.nextInt(-this.radius+2, this.radius-2); spawnPosition = new Pos(x, 1, z).add(0.5, 0, 0.5); From 35dc924104b7ae0f30de48457e79a351bef3203f Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:30:11 +0200 Subject: [PATCH 33/38] added null ckeck for boostTask and boostRefillTask --- .../game/stateless/types/turtleGame/gameObjects/Turtle.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java index 2f9328b..539ee8d 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -46,8 +46,8 @@ public class Turtle extends EntityCreature { this.removePassenger(this.player); this.remove(); this.kill(); - if(this.boostRefillTask.isAlive()) this.boostRefillTask.cancel(); - if(this.boostTask.isAlive()) this.boostTask.cancel(); + if(this.boostRefillTask != null && this.boostRefillTask.isAlive()) this.boostRefillTask.cancel(); + if(this.boostTask != null && this.boostTask.isAlive()) this.boostTask.cancel(); } public void adaptView() { @@ -82,7 +82,7 @@ public class Turtle extends EntityCreature { } public void cancelBoost() { - if(!this.boostTask.isAlive()) return; + if(this.boostTask == null || !this.boostTask.isAlive()) return; this.boostTask.cancel(); this.boostSpeedMultiplier = 1; } From 6d8c5ed917226d50f14b360c0d766c41e2cd16c7 Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:33:17 +0200 Subject: [PATCH 34/38] solved pr comments --- .../game/stateless/types/turtleGame/gameObjects/Turtle.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java index 539ee8d..cbcd537 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -30,8 +30,7 @@ public class Turtle extends EntityCreature { } public void spawnTurtle() { - this.setInstance(this.player.getInstance()); - this.teleport(this.player.getPosition()); + this.setInstance(this.player.getInstance(), this.player.getPosition()); this.addPassenger(this.player); } @@ -45,7 +44,6 @@ public class Turtle extends EntityCreature { public void destroy() { this.removePassenger(this.player); this.remove(); - this.kill(); if(this.boostRefillTask != null && this.boostRefillTask.isAlive()) this.boostRefillTask.cancel(); if(this.boostTask != null && this.boostTask.isAlive()) this.boostTask.cancel(); } @@ -76,7 +74,6 @@ public class Turtle extends EntityCreature { return; } this.boostChargeLevel = Math.max(0f, this.boostChargeLevel - 0.025f); - System.out.println(this.boostChargeLevel); this.player.setExp(this.boostChargeLevel); }, TaskSchedule.millis(30), TaskSchedule.millis(30)); } From 9f71523a0763207263a57462b838e7c0d9288d75 Mon Sep 17 00:00:00 2001 From: lars Date: Wed, 15 Oct 2025 21:56:48 +0200 Subject: [PATCH 35/38] lowered snack count --- .../instance/game/stateless/types/turtleGame/TurtleGame.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 47d7754..c2ab1b3 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -242,7 +242,7 @@ class TurtleGame extends StatelessGame { @Override protected void onStart() { - this.generateNewSnacks((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); + this.generateNewSnacks(this.turtlePlayerMap.size() + 1); this.generateNewBombs((int) Math.ceil(this.snacks.size() * 0.5)); this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill); } From be6b6da68eb162a06218785b7e737f63187ffa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 22:01:10 +0200 Subject: [PATCH 36/38] fix: prevent null instance in Turtle adaptView method --- .../game/stateless/types/turtleGame/gameObjects/Turtle.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java index cbcd537..5ba3916 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -49,6 +49,7 @@ public class Turtle extends EntityCreature { } public void adaptView() { + if(this.getInstance() == null) return; this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch()))); Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100); this.lookAt(this.getPosition().add(lookingVector.asPosition())); From d083ca3e1a4914744813d4ee0428624f7fe91bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 15 Oct 2025 22:57:45 +0200 Subject: [PATCH 37/38] fix: include '!' in regex for language file processing --- src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java b/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java index 66bf0bb..0c70f86 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java +++ b/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java @@ -53,7 +53,7 @@ public class Languages { for(String line : Files.readAllLines(locale.toPath())) { //line = line.replaceAll("[^\\p{L}\\s,#_+.:;]+", ""); - line = line.replaceAll("[^a-zA-Z0-9äöüÄÖÜ ,:;#_+]", ""); + line = line.replaceAll("[^a-zA-Z0-9äöüÄÖÜ ,:;#!_+]", ""); String[] columns = line.split(";"); if(columns.length < 1) continue; From c301e775c90e3b5b92da35a7331870c03d2b9e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Thu, 16 Oct 2025 00:24:48 +0200 Subject: [PATCH 38/38] added translation to all messages, ChatFormatHandler and new chat icons --- .../minenet/minigames/command/Commands.java | 7 +- .../command/privileged/FakeplayerCommand.java | 31 --- .../privileged/SetRoomOwnerCommand.java | 4 +- .../minenet/minigames/handler/Listeners.java | 3 +- .../handler/global/ChatFormatHandler.java | 20 ++ .../handler/global/PlayerLeaveHandler.java | 10 +- .../handler/global/PlayerLoginHandler.java | 2 - .../minenet/minigames/instance/game/Game.java | 21 +- .../game/stateless/StatelessGame.java | 28 ++- .../types/elytraRace/ElytraRace.java | 206 +++++++++--------- .../instance/hub/inventory/JoinInventory.java | 6 +- .../minenet/minigames/instance/room/Room.java | 14 +- .../instance/room/entity/GameSelector.java | 4 +- .../mhsl/minenet/minigames/message/Icon.java | 5 +- .../message/TranslatableMessage.java | 17 +- src/main/resources/lang/locales.map.csv | 13 ++ 16 files changed, 225 insertions(+), 166 deletions(-) delete mode 100644 src/main/java/eu/mhsl/minenet/minigames/command/privileged/FakeplayerCommand.java create mode 100644 src/main/java/eu/mhsl/minenet/minigames/handler/global/ChatFormatHandler.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java b/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java index a9a6a90..0c99f7e 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java @@ -21,7 +21,6 @@ public enum Commands { ROOM(new RoomCommand()), UPDATE(new RefreshCommandsCommand()), OP(new OpCommand()), - FAKEPLAYER(new FakeplayerCommand()), KICK(new KickCommand()), SKIN(new SkinCommand()), SETOWNER(new SetRoomOwnerCommand()), @@ -41,7 +40,11 @@ public enum Commands { static { MinecraftServer.getCommandManager().setUnknownCommandCallback((sender, command) -> { if(command.isBlank()) return; - new ChatMessage(Icon.ERROR).appendStatic("Unknown command: ").quote(command).send(sender); + new ChatMessage(Icon.ERROR) + .appendTranslated("common#unknownCommand") + .appendSpace() + .quote(command) + .send(sender); }); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/FakeplayerCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/FakeplayerCommand.java deleted file mode 100644 index 69c31c5..0000000 --- a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/FakeplayerCommand.java +++ /dev/null @@ -1,31 +0,0 @@ -package eu.mhsl.minenet.minigames.command.privileged; - -import eu.mhsl.minenet.minigames.command.PrivilegedCommand; -import eu.mhsl.minenet.minigames.instance.room.Room; -import eu.mhsl.minenet.minigames.message.Icon; -import eu.mhsl.minenet.minigames.message.type.ChatMessage; -import net.minestom.server.command.builder.arguments.ArgumentType; -import net.minestom.server.entity.Player; - -import java.util.UUID; - -public class FakeplayerCommand extends PrivilegedCommand { - public FakeplayerCommand() { - super("fakeplayer"); - - addSyntax((sender, context) -> { - if(sender instanceof Player p) { - if(p.getInstance() instanceof Room room) { -// FakePlayer.initPlayer( // TODO FakePlayer does no longer exists -// UUID.randomUUID(), -// context.getRaw("name"), -// new FakePlayerOption().setInTabList(true).setRegistered(true), -// fakePlayer -> Room.setRoom(fakePlayer, room) -// ); - } else { - new ChatMessage(Icon.ERROR).appendStatic("Du musst dich in einer Raumlobby befinden!").send(sender); - } - } - }, ArgumentType.String("name")); - } -} diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SetRoomOwnerCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SetRoomOwnerCommand.java index 6a83853..85cf006 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SetRoomOwnerCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SetRoomOwnerCommand.java @@ -20,7 +20,7 @@ public class SetRoomOwnerCommand extends PrivilegedCommand { setDefaultExecutor((sender, context) -> { if(sender instanceof Player p) { Room.getRoom(p).orElseThrow().setOwner(p); - new ChatMessage(Icon.SUCCESS).appendStatic("You are now the owner of this room!").send(sender); + new ChatMessage(Icon.SUCCESS).appendTranslated("room#ownerSelf").send(sender); } }); @@ -29,7 +29,7 @@ public class SetRoomOwnerCommand extends PrivilegedCommand { if(sender instanceof Player p) { Player newOwner = MinecraftServer.getConnectionManager().getOnlinePlayerByUsername(context.getRaw("player")); Room.getRoom(p).orElseThrow().setOwner(Objects.requireNonNull(newOwner)); - new ChatMessage(Icon.SUCCESS).appendStatic("The new owner has been set!").send(sender); + new ChatMessage(Icon.SUCCESS).appendTranslated("room#ownerSet").send(sender); } }, ArgumentType.Entity("player").onlyPlayers(true)); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java b/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java index 72b17c0..2794283 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java @@ -7,7 +7,8 @@ import net.minestom.server.event.EventListener; public enum Listeners { SPAWN(new AddEntityToInstanceEventListener()), LOGIN(new PlayerLoginHandler()), - LEAVE(new PlayerLeaveHandler()); + LEAVE(new PlayerLeaveHandler()), + CHAT(new ChatFormatHandler()); Listeners(EventListener event) { MinecraftServer.getGlobalEventHandler().addListener(event); diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/global/ChatFormatHandler.java b/src/main/java/eu/mhsl/minenet/minigames/handler/global/ChatFormatHandler.java new file mode 100644 index 0000000..f548248 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/global/ChatFormatHandler.java @@ -0,0 +1,20 @@ +package eu.mhsl.minenet.minigames.handler.global; + +import eu.mhsl.minenet.minigames.message.Icon; +import eu.mhsl.minenet.minigames.message.type.ChatMessage; +import net.minestom.server.event.EventListener; +import net.minestom.server.event.player.PlayerChatEvent; +import org.jetbrains.annotations.NotNull; + +public class ChatFormatHandler implements EventListener { + @Override + public @NotNull Class eventType() { + return PlayerChatEvent.class; + } + + @Override + public @NotNull Result run(@NotNull PlayerChatEvent event) { + event.setFormattedMessage(new ChatMessage(Icon.CHAT).appendStatic(event.getRawMessage()).build(event.getPlayer())); + return Result.SUCCESS; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLeaveHandler.java b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLeaveHandler.java index fcfc4bb..afa771e 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLeaveHandler.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLeaveHandler.java @@ -1,5 +1,9 @@ package eu.mhsl.minenet.minigames.handler.global; +import eu.mhsl.minenet.minigames.message.Icon; +import eu.mhsl.minenet.minigames.message.type.ChatMessage; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.event.EventListener; import net.minestom.server.event.player.PlayerDisconnectEvent; @@ -14,7 +18,11 @@ public class PlayerLeaveHandler implements EventListener @Override public @NotNull Result run(@NotNull PlayerDisconnectEvent event) { Player p = event.getPlayer(); -// new ChatMessage(Icon.SCIENCE).appendStatic("unübersetzter Leavetext: ").appendStatic(p.getDisplayName()).send(MinecraftServer.getConnectionManager().getOnlinePlayers()); + new ChatMessage(Icon.LEAVE) + .appendStatic(p.getName().color(NamedTextColor.GRAY)) + .appendSpace() + .appendTranslated("common#leave", NamedTextColor.DARK_GRAY) + .send(MinecraftServer.getConnectionManager().getOnlinePlayers()); return Result.SUCCESS; } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java index b694668..fa8144f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java @@ -65,8 +65,6 @@ public class PlayerLoginHandler implements EventListener> options, Player owner) { try { - - Game game = factory.manufacture(Room.getRoom(owner).orElseThrow(), options); + Room originRoom = Room.getRoom(owner).orElseThrow(); + Game game = factory.manufacture(originRoom, options); game.load(); - Room.getRoom(owner).orElseThrow().moveMembersToInstance(game); - - MinecraftServer.getSchedulerManager().scheduleTask(() -> { - game.getPlayers().forEach(player -> new ChatMessage(Icon.SCIENCE) - .appendStatic(factory.name().getAssembled(player).asComponent()) - .newLine() - .appendStatic(factory.description().getAssembled(player).asComponent()) - .send(player)); - - return TaskSchedule.stop(); - }, TaskSchedule.seconds(3)); + originRoom.moveMembersToInstance(game); + new ChatMessage(Icon.INFO) + .appendTranslated(factory.name()) + .newLine() + .appendTranslated(factory.description()) + .send(originRoom.getAllMembers()); } catch (Exception e) { new ChatMessage(Icon.ERROR).appendStatic("Instance crashed: " + e.getMessage()).send(owner); diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java index e318dd5..36aa3f1 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java @@ -57,7 +57,11 @@ public class StatelessGame extends Game { int timeLeft = timeLimit - timePlayed; switch (timeLeft) { case 90, 60, 30, 10, 5, 4, 3, 2, 1 -> - new ChatMessage(Icon.SCIENCE).appendStatic("Noch " + timeLeft + " Sekunden!").send(getPlayers()); + new ChatMessage(Icon.TIME) + .appendStatic(String.valueOf(timeLeft)) + .appendSpace() + .appendTranslated(timeLeft == 1 ? "common#secondsLeft_singular" : "common#secondsLeft_plural") + .send(getPlayers()); } timePlayed++; @@ -77,13 +81,23 @@ public class StatelessGame extends Game { * When overriding make sure to call this::start after countdown! */ protected CompletableFuture countdownStart() { - return new Countdown(TitleMessage.class) - .countdown(Audience.audience(getPlayers()), 5, countdownModifier -> countdownModifier.message = new TitleMessage(Duration.ofMillis(300), Duration.ofMillis(700)) - .subtitle(subtitleMessage -> subtitleMessage.appendStatic(Component.text("in ", NamedTextColor.DARK_GREEN)) - .appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN)) - .appendStatic(Component.text(" seconds", NamedTextColor.DARK_GREEN)))); - } + Duration fadeIn = Duration.ofMillis(300); + Duration stay = Duration.ofMillis(700); + return new Countdown(TitleMessage.class) + .countdown( + Audience.audience(this.getPlayers()), + 5, + modifier -> modifier.message = new TitleMessage(fadeIn, stay) + .subtitle(subtitleMessage -> subtitleMessage + .appendTranslated("common#startIn", NamedTextColor.DARK_GREEN) + .appendSpace() + .appendStatic(Component.text(modifier.timeLeft, NamedTextColor.GREEN)) + .appendSpace() + .appendTranslated(modifier.timeLeft == 1 ? "common#second" : "common#seconds", NamedTextColor.DARK_GREEN) + ) + ); + } public void startAccessor() { this.start(); diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java index 9f7a446..c36725a 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java @@ -20,7 +20,10 @@ import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.EquipmentSlot; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; -import net.minestom.server.event.player.*; +import net.minestom.server.event.player.PlayerMoveEvent; +import net.minestom.server.event.player.PlayerStartFlyingWithElytraEvent; +import net.minestom.server.event.player.PlayerStopFlyingWithElytraEvent; +import net.minestom.server.event.player.PlayerUseItemEvent; import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; @@ -49,80 +52,74 @@ public class ElytraRace extends StatelessGame { private final Material resetMaterial = Material.RED_DYE; private final int boostMultiplier = 50; private final Material ringMaterial = Material.GOLD_BLOCK; - private int generatedUntil = 0; - - private record CheckPointData(int currentCheckpoint, int nextCheckpoint) { - public CheckPointData next(int spacing) { - return new CheckPointData(nextCheckpoint, nextCheckpoint + spacing); - } - } private final Map playerCheckpoints = new HashMap<>(); + private int generatedUntil = 0; public ElytraRace(int ringCount) { super(Dimension.OVERWORLD.key, "ElytraRace", new FirstWinsScore()); this.ringCount = ringCount; - setGenerator(vale); - vale.setCalculateSeaLevel(point -> seaLevel); - vale.setXShiftMultiplier(integer -> NumberUtil.map(integer, 50, 500, 0, 1)); - vale.addMixIn(new PlaneTerrainGenerator(gameHeight, Block.BARRIER)); + this.setGenerator(this.vale); + this.vale.setCalculateSeaLevel(point -> this.seaLevel); + this.vale.setXShiftMultiplier(integer -> NumberUtil.map(integer, 50, 500, 0, 1)); + this.vale.addMixIn(new PlaneTerrainGenerator(this.gameHeight, Block.BARRIER)); - eventNode().addListener(PlayerUseItemEvent.class, playerUseItemEvent -> { + this.eventNode().addListener(PlayerUseItemEvent.class, playerUseItemEvent -> { Player player = playerUseItemEvent.getPlayer(); Material usedMaterial = playerUseItemEvent.getItemStack().material(); - if(usedMaterial.equals(boostMaterial)) { - if(!player.isFlyingWithElytra()) return; + if (usedMaterial.equals(this.boostMaterial)) { + if (!player.isFlyingWithElytra()) return; - boost(player); - InventoryUtil.removeItemFromPlayer(player, boostMaterial, 1); - } else if(usedMaterial.equals(resetMaterial)) { - toCheckpoint(player); - InventoryUtil.removeItemFromPlayer(player, resetMaterial, 1); + this.boost(player); + InventoryUtil.removeItemFromPlayer(player, this.boostMaterial, 1); + } else if (usedMaterial.equals(this.resetMaterial)) { + this.toCheckpoint(player); + InventoryUtil.removeItemFromPlayer(player, this.resetMaterial, 1); } }); - eventNode().addListener(PlayerStopFlyingWithElytraEvent.class, playerStopFlyingWithElytraEvent -> { + this.eventNode().addListener(PlayerStopFlyingWithElytraEvent.class, playerStopFlyingWithElytraEvent -> { Player player = playerStopFlyingWithElytraEvent.getPlayer(); - if(Position.blocksBelowPlayer(this, player).contains(ringMaterial.block())) { + if (Position.blocksBelowPlayer(this, player).contains(this.ringMaterial.block())) { player.setFlyingWithElytra(true); - boost(player); + this.boost(player); } else { - toCheckpoint(player); + this.toCheckpoint(player); // getScore().insertResult(playerStopFlyingWithElytraEvent.getPlayer()); // playerStopFlyingWithElytraEvent.getPlayer().setGameMode(GameMode.SPECTATOR); } }); - eventNode().addListener(PlayerStartFlyingWithElytraEvent.class, playerStartFlyingWithElytraEvent -> { - if(!isRunning) { + this.eventNode().addListener(PlayerStartFlyingWithElytraEvent.class, playerStartFlyingWithElytraEvent -> { + if (!this.isRunning) { playerStartFlyingWithElytraEvent.getPlayer().setFlyingWithElytra(false); return; } - boost(playerStartFlyingWithElytraEvent.getPlayer()); + this.boost(playerStartFlyingWithElytraEvent.getPlayer()); }); } @Override protected void onLoad(@NotNull CompletableFuture callback) { - Point spawnpoint = new Pos(vale.getXShiftAtZ(0), -46, 0); - GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> setBlock(point, BlockPallet.STREET.rnd())); + Point spawnpoint = new Pos(this.vale.getXShiftAtZ(0), -46, 0); + GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> this.setBlock(point, BlockPallet.STREET.rnd())); - generateRing(ringSpacing); - generateRing(ringSpacing * 2); + this.generateRing(this.ringSpacing); + this.generateRing(this.ringSpacing * 2); callback.complete(null); } @Override protected void onStart() { - getPlayers().forEach(player -> { + this.getPlayers().forEach(player -> { player.getInventory().setEquipment(EquipmentSlot.CHESTPLATE, (byte) 0, ItemStack.of(Material.ELYTRA)); - for(int i = 0; i < 3; i++) { - player.getInventory().setItemStack(i, ItemStack.builder(boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build()); + for (int i = 0; i < 3; i++) { + player.getInventory().setItemStack(i, ItemStack.builder(this.boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build()); } - addResetItemToPlayer(player); + this.addResetItemToPlayer(player); }); } @@ -131,44 +128,44 @@ public class ElytraRace extends StatelessGame { Player player = playerMoveEvent.getPlayer(); Point newPos = playerMoveEvent.getNewPosition(); - if(isBeforeBeginning && playerMoveEvent.getNewPosition().y() < getSpawn().y()) { - player.teleport(getSpawn()); + if (this.isBeforeBeginning && playerMoveEvent.getNewPosition().y() < this.getSpawn().y()) { + player.teleport(this.getSpawn()); return; } - playerCheckpoints.putIfAbsent(player, new CheckPointData(ringSpacing, ringSpacing * 2)); + this.playerCheckpoints.putIfAbsent(player, new CheckPointData(this.ringSpacing, this.ringSpacing * 2)); - if(newPos.z() > generatedUntil - ringSpacing) { - generateRing(generatedUntil + ringSpacing); + if (newPos.z() > this.generatedUntil - this.ringSpacing) { + this.generateRing(this.generatedUntil + this.ringSpacing); } - if(newPos.z() > playerCheckpoints.get(player).nextCheckpoint) { - playerCheckpoints.put(player, playerCheckpoints.get(player).next(ringSpacing)); - boost(player); + if (newPos.z() > this.playerCheckpoints.get(player).nextCheckpoint) { + this.playerCheckpoints.put(player, this.playerCheckpoints.get(player).next(this.ringSpacing)); + this.boost(player); } - if(newPos.y() > gameHeight - 5) { - Point particlePoint = newPos.withY(gameHeight); + if (newPos.y() > this.gameHeight - 5) { + Point particlePoint = newPos.withY(this.gameHeight); ParticlePacket particle = new ParticlePacket( - Particle.WAX_ON, - particlePoint.blockX(), - particlePoint.blockY(), - particlePoint.withZ(z -> z+10).blockZ(), - 20, - 0, - 30, - 1f, - Math.toIntExact((long) NumberUtil.map(newPos.y(), gameHeight - 5, gameHeight, 50, 500)) + Particle.WAX_ON, + particlePoint.blockX(), + particlePoint.blockY(), + particlePoint.withZ(z -> z + 10).blockZ(), + 20, + 0, + 30, + 1f, + Math.toIntExact((long) NumberUtil.map(newPos.y(), this.gameHeight - 5, this.gameHeight, 50, 500)) ); player.sendPacket(particle); } - if(getBlock(player.getPosition()).equals(Block.WATER)) { - toCheckpoint(player); + if (this.getBlock(player.getPosition()).equals(Block.WATER)) { + this.toCheckpoint(player); } - if(newPos.z() > ringCount * ringSpacing) { - getScore().insertResult(player); + if (newPos.z() > this.ringCount * this.ringSpacing) { + this.getScore().insertResult(player); player.setGameMode(GameMode.SPECTATOR); player.setFlyingWithElytra(false); } @@ -176,43 +173,44 @@ public class ElytraRace extends StatelessGame { @Override public Pos getSpawn() { - return new Pos(vale.getXShiftAtZ(0), -45, 0); + return new Pos(this.vale.getXShiftAtZ(0), -45, 0); } private void addResetItemToPlayer(Player p) { - p.getInventory().setItemStack(8, ItemStack.builder(resetMaterial).customName(TranslatedComponent.byId("reset").getAssembled(p)).build()); + p.getInventory().setItemStack(8, ItemStack.builder(this.resetMaterial).customName(TranslatedComponent.byId("reset").getAssembled(p)).build()); } private Point getRingPositionAtZ(int z) { Random random = new Random(this.hashCode() + z); - return new Pos(vale.getXShiftAtZ(z), -45 + random.nextInt(-5, 15), z); + return new Pos(this.vale.getXShiftAtZ(z), -45 + random.nextInt(-5, 15), z); } private CompletableFuture generateRing(int zPos) { - if(zPos > ringCount * ringSpacing) return null; - boolean isLast = (zPos == ringCount * ringSpacing); + if (zPos > this.ringCount * this.ringSpacing) return null; + boolean isLast = (zPos == this.ringCount * this.ringSpacing); - generatedUntil = zPos; + this.generatedUntil = zPos; AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); - Point ringPos = getRingPositionAtZ(zPos); + Point ringPos = this.getRingPositionAtZ(zPos); GeneratorUtils.iterateArea( - ringPos.sub(100, 0, 0).withY(0), - ringPos.add(100, 0, 0).withY(seaLevel), - point -> batch.setBlock(point, Block.BARRIER) + ringPos.sub(100, 0, 0).withY(0), + ringPos.add(100, 0, 0).withY(this.seaLevel), + point -> batch.setBlock(point, Block.BARRIER) ); GeneratorUtils.iterateArea( - ringPos.sub(3, 3, 0), - ringPos.add(3, 3, 0), - point -> batch.setBlock(point, isLast ? Block.DIAMOND_BLOCK : ringMaterial.block()) + ringPos.sub(3, 3, 0), + ringPos.add(3, 3, 0), + point -> batch.setBlock(point, isLast ? Block.DIAMOND_BLOCK : this.ringMaterial.block()) ); GeneratorUtils.iterateArea( - ringPos.sub(2, 2, 0), - ringPos.add(2, 2, 0), - point -> batch.setBlock(point, Block.AIR) + ringPos.sub(2, 2, 0), + ringPos.add(2, 2, 0), + point -> batch.setBlock(point, Block.AIR) ); - BatchUtil.loadAndApplyBatch(batch, this, () -> {}); + BatchUtil.loadAndApplyBatch(batch, this, () -> { + }); return null; } @@ -221,13 +219,13 @@ public class ElytraRace extends StatelessGame { Vec playerVelocity = player.getPosition().direction(); player.setVelocity( - player.getVelocity().add(playerVelocity.mul(boostMultiplier)) - .withY(playerVelocity.withY(v -> v * boostMultiplier).y()) + player.getVelocity().add(playerVelocity.mul(this.boostMultiplier)) + .withY(playerVelocity.withY(v -> v * this.boostMultiplier).y()) ); } private void toCheckpoint(Player p) { - Point checkpointPos = getRingPositionAtZ(playerCheckpoints.get(p).currentCheckpoint); + Point checkpointPos = this.getRingPositionAtZ(this.playerCheckpoints.get(p).currentCheckpoint); p.setVelocity(Vec.ZERO); p.setFlyingWithElytra(false); p.teleport(Pos.fromPoint(checkpointPos).add(0.5, 0, 0.5)); @@ -236,27 +234,35 @@ public class ElytraRace extends StatelessGame { p.setFlyingSpeed(0); new Countdown(TitleMessage.class) - .countdown( - Audience.audience(p), - 3, - countdownModifier -> - countdownModifier.message = new TitleMessage( - Duration.ofMillis(300), - Duration.ofMillis(700) - ) - .subtitle( - subtitleMessage -> - subtitleMessage - .appendStatic(Component.text("Launch in ", NamedTextColor.DARK_GREEN)) - .appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN)) - .appendStatic(Component.text(" seconds", NamedTextColor.DARK_GREEN)) - ) - ).thenRun(() -> { - p.setFlying(false); - p.setFlyingSpeed(1); - p.setFlyingWithElytra(true); - boost(p); - addResetItemToPlayer(p); - }); + .countdown( + Audience.audience(p), + 3, + countdownModifier -> + countdownModifier.message = new TitleMessage( + Duration.ofMillis(300), + Duration.ofMillis(700) + ) + .subtitle( + subtitleMessage -> + subtitleMessage + .appendTranslated("game_ElytraRace#launchIn") + .appendSpace() + .appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN)) + .appendSpace() + .appendTranslated(countdownModifier.timeLeft == 1 ? "common#second" : "common#seconds") + ) + ).thenRun(() -> { + p.setFlying(false); + p.setFlyingSpeed(1); + p.setFlyingWithElytra(true); + this.boost(p); + this.addResetItemToPlayer(p); + }); + } + + private record CheckPointData(int currentCheckpoint, int nextCheckpoint) { + public CheckPointData next(int spacing) { + return new CheckPointData(this.nextCheckpoint, this.nextCheckpoint + spacing); + } } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/hub/inventory/JoinInventory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/hub/inventory/JoinInventory.java index 467edcb..4244894 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/hub/inventory/JoinInventory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/hub/inventory/JoinInventory.java @@ -55,7 +55,11 @@ public class JoinInventory extends InteractableInventory { if(target.isPresent()) Room.setRoom(player, target.get()); else - new ChatMessage(Icon.ERROR).appendTranslated("hub#join_notFound").appendStatic(" " + typedText).send(player); + new ChatMessage(Icon.ERROR) + .appendTranslated("hub#join_notFound") + .appendSpace() + .quote(typedText.trim()) + .send(player); } private String formatInput(String raw) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java b/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java index 6b525f3..cc2f0df 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java @@ -135,9 +135,17 @@ public class Room extends MineNetInstance implements Spawnable { Room.unsetRoom(p); - 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); + new ChatMessage(Icon.ERROR) + .appendTranslated("room#ownerLeft") + .send(this.getAllMembers()); + new ChatMessage(Icon.SCIENCE) + .appendStatic(this.owner.getUsername()) + .appendSpace() + .appendTranslated("room#newOwnerAnnounce") + .send(this.getAllMembers().stream().filter(player -> player != this.owner).collect(Collectors.toSet())); + new ChatMessage(Icon.SUCCESS) + .appendTranslated("room#ownerSelf") + .send(this.owner); }); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/room/entity/GameSelector.java b/src/main/java/eu/mhsl/minenet/minigames/instance/room/entity/GameSelector.java index 7603fe7..8ad6ac9 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/room/entity/GameSelector.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/room/entity/GameSelector.java @@ -37,7 +37,9 @@ public class GameSelector extends InteractableEntity { if(playerEntityInteractEvent.getPlayer() != room.getOwner()) { abstractVillagerMeta.setHeadShakeTimer(20); - new ChatMessage(Icon.ERROR).appendStatic("Only the room leader can start games!").send(playerEntityInteractEvent.getPlayer()); + new ChatMessage(Icon.ERROR) + .appendTranslated("room#onlyOwnerCanStart") + .send(playerEntityInteractEvent.getPlayer()); return; } diff --git a/src/main/java/eu/mhsl/minenet/minigames/message/Icon.java b/src/main/java/eu/mhsl/minenet/minigames/message/Icon.java index 35efac2..0879e21 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/message/Icon.java +++ b/src/main/java/eu/mhsl/minenet/minigames/message/Icon.java @@ -8,7 +8,10 @@ public enum Icon { STAR("\u2606", NamedTextColor.GOLD), CHAT("\u276F\u276F", NamedTextColor.WHITE), SUCCESS("\u2714", NamedTextColor.GREEN), - ERROR("\u274C", NamedTextColor.RED); + ERROR("\u274C", NamedTextColor.RED), + TIME("\u231B", NamedTextColor.YELLOW), + INFO("\uD83D\uDD14", NamedTextColor.AQUA), + LEAVE("\u2B05", NamedTextColor.DARK_GRAY); private final String symbol; private final NamedTextColor color; diff --git a/src/main/java/eu/mhsl/minenet/minigames/message/TranslatableMessage.java b/src/main/java/eu/mhsl/minenet/minigames/message/TranslatableMessage.java index 855ee5c..a485896 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/message/TranslatableMessage.java +++ b/src/main/java/eu/mhsl/minenet/minigames/message/TranslatableMessage.java @@ -31,8 +31,23 @@ public abstract class TranslatableMessage implements Sendable { return this; } + public TranslatableMessage appendSpace() { + chain.add(Component.text(" ")); + return this; + } + public TranslatableMessage appendTranslated(String mapId) { - chain.add(TranslatedComponent.byId(mapId)); + chain.add(TranslatedComponent.byId(mapId).setColor(NamedTextColor.WHITE)); + return this; + } + + public TranslatableMessage appendTranslated(String mapId, NamedTextColor color) { + chain.add(TranslatedComponent.byId(mapId).setColor(color)); + return this; + } + + public TranslatableMessage appendTranslated(TranslatedComponent component) { + chain.add(component); return this; } diff --git a/src/main/resources/lang/locales.map.csv b/src/main/resources/lang/locales.map.csv index 5de9337..135c512 100644 --- a/src/main/resources/lang/locales.map.csv +++ b/src/main/resources/lang/locales.map.csv @@ -9,6 +9,13 @@ select_language;Please select your prefered Language;Bitte wähle deine bevorzug welcome;Welcome!;Willkommen! back;Back;Zurück forward;Next;Nächste +leave;left the game;hat das Spiel verlassen +secondsLeft_singular;second left;Sekunde verbleibt +secondsLeft_plural;seconds left;Sekunden verbleiben +unknownCommand;Unknown command;Unbekannter Befehl +startIn;Start in;Startet in +second;second;Sekunde +seconds;seconds;Sekunden ;; ns:tablist#UNUSED;; title;MineNet Network;MineNet Servernetzwerk @@ -65,6 +72,11 @@ ns:room#;; invTitle;Select a Minigame;Wähle einen Spielmodus noOption;No options here;Keine Optionen hier noOptionDescription;There are no options for this Game;Es gibt keine Einstellungen für dieses Spiel +ownerSelf;You are now the owner of this room!;Du bist nun Besitzer dieser Lobby! +ownerSet;The new owner has been set!;Der neue Besitzer wurde festgelegt! +ownerLeft;The room owner has left!;Der Lobbybesitzer hat das Spiel verlassen! +newOwnerAnnounce;is the new owner!;ist der neue Besitzer +onlyOwnerCanStart;Only the room leader can start games!;Nur der Raumbesitzer kann Spiele starten! ;; ns:GameFactory#;; missingDescription;No description;Keine Beschreibung @@ -78,6 +90,7 @@ ns:game_ElytraRace#;; name;Elytra race;Elytra Rennen description;Be fast while flying through the rings;Sei schnell während du durch die Ringe fliegst ringCount;ring count;Anzahl der Ringe +launchIn;Launch in;Abschuss in ;; ns:game_Minerun#;; name;Minerun;Minenrennen