47 Commits

Author SHA1 Message Date
0b65a62016 Merge branch 'develop' into develop-fairScoreboard 2025-10-15 22:25:24 +00:00
c301e775c9 added translation to all messages, ChatFormatHandler and new chat icons 2025-10-16 00:24:48 +02:00
d083ca3e1a fix: include '!' in regex for language file processing 2025-10-15 22:57:45 +02:00
50200b46ae Merge pull request 'develop-turtleGame' (#6) from develop-turtleGame into develop
Reviewed-on: #6
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2025-10-15 20:21:27 +00:00
46a0f6e5be Merge remote-tracking branch 'origin/develop-turtleGame' into develop-turtleGame 2025-10-15 22:20:14 +02:00
758c51a2e1 Merge remote-tracking branch 'origin/develop' into develop-turtleGame
# Conflicts:
#	src/main/resources/lang/locales.map.csv
2025-10-15 22:19:39 +02:00
fd3f51c018 Merge pull request 'added SpaceSnake' (#5) from develop-spaceSnake into develop
Reviewed-on: #5
Reviewed-by: Lars Neuhaus <larslukasneuhaus@gmx.de>
2025-10-15 20:11:37 +00:00
be6b6da68e fix: prevent null instance in Turtle adaptView method 2025-10-15 22:01:10 +02:00
9f71523a07 lowered snack count 2025-10-15 21:56:48 +02:00
6d8c5ed917 solved pr comments 2025-10-15 21:33:17 +02:00
35dc924104 added null ckeck for boostTask and boostRefillTask 2025-10-15 21:30:11 +02:00
eff5e36987 solved some pr comments 2025-10-15 21:26:02 +02:00
9e2125cba3 Merge remote-tracking branch 'origin/develop' into develop-turtleGame
# Conflicts:
#	src/main/resources/lang/locales.map.csv
2025-10-15 21:06:04 +02:00
c8bf5f9186 added turtle game to pve 2025-10-15 21:04:46 +02:00
1830307f4b increased boost refill when eating flowers, changed speed options 2025-10-15 21:03:12 +02:00
3dfff84c61 changed left player detection to use hasResult of Score 2025-10-15 20:55:30 +02:00
6076c0ca15 Refactored player state management in SpaceSnake and implemented endgame handling 2025-10-15 20:54:26 +02:00
41028e3389 SpaceSnake displayBlock to Fallingblock
powerup detection via boundingbox
2025-10-15 20:36:52 +02:00
bc3f5f58a4 prevent diamond block as building block in SpaceSnake 2025-10-15 19:28:48 +02:00
512805de05 Merge branch 'develop' into develop-spaceSnake 2025-10-15 00:52:30 +02:00
9596800889 fixed extra space in lambda expression in SpaceSnake spawn calculation 2025-10-15 00:51:50 +02:00
a8a15a1c7c adjusted SpaceSnake spawn position and random bounds 2025-10-15 00:51:08 +02:00
db78ff33ce fixed typo in game name: renamed SNAKE3D to SPACESNAKE 2025-10-15 00:46:34 +02:00
5bb07596a1 added SpaceSnake game and related assets 2025-10-15 00:42:11 +02:00
c4aaa7acf9 added flowers and grass to HeightTerrainGenerator 2025-10-11 15:17:23 +02:00
097438886c added stickfight start and working length option 2025-10-11 12:22:50 +02:00
d5910b4b54 renamed methods 2025-10-06 17:40:08 +02:00
ec76dd5c85 added boost charge when eating snacks 2025-10-06 17:31:04 +02:00
84de61388e improved speed mechanic and bomb spawning, added countdown for last player 2025-10-05 18:24:46 +02:00
dece9c13b7 added translations 2025-10-05 00:19:37 +02:00
39fb7f4956 added boost mechanic 2025-10-05 00:13:56 +02:00
75314748da removed sidebar, added names for items 2025-10-04 18:32:27 +02:00
61aa7543be improved sidebar (ordered and colored) 2025-10-04 16:29:03 +02:00
2fac287e1e added sidebar 2025-10-04 16:11:10 +02:00
2a6f2f2a44 added particle effects and sounds 2025-10-04 15:39:19 +02:00
382d850605 added better speed mechanic 2025-10-04 14:03:44 +02:00
a49b3b20cc Merge remote-tracking branch 'origin/develop' into develop-turtleGame 2025-10-04 13:53:17 +02:00
abf907af24 added todos 2025-10-04 13:49:49 +02:00
8bd0ab1974 fixed error after game ends 2025-10-04 13:39:57 +02:00
2c92553a8a added bombs 2025-10-04 13:15:57 +02:00
f26c3a9e6d improved entity collision check while generating 2025-10-04 00:36:50 +02:00
81524cfecf added snacks with collision, switched to multiplayer arena 2025-10-04 00:09:19 +02:00
f2fc4835c3 started random snack generation 2025-10-03 19:32:52 +02:00
abcb23d96a made turtle movement clean 2025-10-03 18:47:11 +02:00
eabbb312b9 Merge remote-tracking branch 'refs/remotes/origin/develop' into develop-turtleGame 2025-10-03 17:29:36 +02:00
d98cebd86f added turtleGame playfield and movement 2025-09-02 22:29:47 +02:00
c87d318421 started turtleGame 2025-09-02 18:22:14 +02:00
28 changed files with 925 additions and 177 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<PlayerChatEvent> {
@Override
public @NotNull Class<PlayerChatEvent> 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;
}
}

View File

@@ -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<PlayerDisconnectEvent>
@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;
}
}

View File

@@ -65,8 +65,6 @@ public class PlayerLoginHandler implements EventListener<AsyncPlayerConfiguratio
Logger.getLogger("user").info(p.getUsername() + " joined");
// new ChatMessage(Icon.SCIENCE).appendStatic("unübersetzter Jointext: ").appendStatic(p.getUsername()).send(MinecraftServer.getConnectionManager().getOnlinePlayers());
return Result.SUCCESS;
}
}

View File

@@ -58,21 +58,16 @@ public abstract class Game extends MineNetInstance implements Spawnable {
public static void initialize(GameFactory factory, List<Option<?>> 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);
originRoom.moveMembersToInstance(game);
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
game.getPlayers().forEach(player -> new ChatMessage(Icon.SCIENCE)
.appendStatic(factory.name().getAssembled(player).asComponent())
new ChatMessage(Icon.INFO)
.appendTranslated(factory.name())
.newLine()
.appendStatic(factory.description().getAssembled(player).asComponent())
.send(player));
return TaskSchedule.stop();
}, TaskSchedule.seconds(3));
.appendTranslated(factory.description())
.send(originRoom.getAllMembers());
} catch (Exception e) {
new ChatMessage(Icon.ERROR).appendStatic("Instance crashed: " + e.getMessage()).send(owner);

View File

@@ -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;
@@ -20,6 +21,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),
@@ -34,13 +36,15 @@ public enum GameList {
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),
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),
SPACESNAKE(new SpaceSnakeFactory(), GameType.PVP);
private final GameFactory factory;
private final GameType type;

View File

@@ -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<Void> 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();

View File

@@ -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<Player, CheckPointData> 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 (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<Void> 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());
player.getInventory().setItemStack(i, ItemStack.builder(this.boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build());
}
addResetItemToPlayer(player);
this.addResetItemToPlayer(player);
});
}
@@ -131,24 +128,24 @@ 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(),
@@ -158,17 +155,17 @@ public class ElytraRace extends StatelessGame {
0,
30,
1f,
Math.toIntExact((long) NumberUtil.map(newPos.y(), gameHeight - 5, gameHeight, 50, 500))
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,35 +173,35 @@ 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<Void> 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),
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())
point -> batch.setBlock(point, isLast ? Block.DIAMOND_BLOCK : this.ringMaterial.block())
);
GeneratorUtils.iterateArea(
ringPos.sub(2, 2, 0),
@@ -212,7 +209,8 @@ public class ElytraRace extends StatelessGame {
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));
@@ -247,16 +245,24 @@ public class ElytraRace extends StatelessGame {
.subtitle(
subtitleMessage ->
subtitleMessage
.appendStatic(Component.text("Launch in ", NamedTextColor.DARK_GREEN))
.appendTranslated("game_ElytraRace#launchIn")
.appendSpace()
.appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN))
.appendStatic(Component.text(" seconds", NamedTextColor.DARK_GREEN))
.appendSpace()
.appendTranslated(countdownModifier.timeLeft == 1 ? "common#second" : "common#seconds")
)
).thenRun(() -> {
p.setFlying(false);
p.setFlyingSpeed(1);
p.setFlyingWithElytra(true);
boost(p);
addResetItemToPlayer(p);
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);
}
}
}

View File

@@ -0,0 +1,182 @@
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;
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.other.FallingBlockMeta;
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.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<Pos> blocks, Material blockType, Pos spawn) {
public void cutToLength(Consumer<Pos> removed) {
while (this.blocks.size() > this.length.get()) {
removed.accept(this.blocks.poll());
}
}
}
private final Map<Player, PlayState> playerStates = new WeakHashMap<>();
private int mapSize;
private final Supplier<Integer> posInBoundsW = () -> this.rnd.nextInt(-this.mapSize/2, this.mapSize/2);
private final Supplier<Integer> posInBoundsH = () -> this.rnd.nextInt(-60, 300);
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 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());
PlayState state = new PlayState(
new AtomicInteger(3),
new ArrayDeque<>(List.of(spawn)),
MaterialUtil.getRandomFullBlock(material -> !material.equals(Material.DIAMOND_BLOCK)),
spawn
);
this.playerStates.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.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));
return;
}
if(playerMoveEvent.getNewPosition().y() < -64) {
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();
}
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent playerBlockPlaceEvent) {
if(this.isBeforeBeginning) {
playerBlockPlaceEvent.setCancelled(true);
return;
}
PlayState state = this.playerStates.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.playerStates.get(player).blockType, 64));
}
private void spawnPowerUp() {
Pos spawnPos = new Pos(this.posInBoundsW.get(), this.posInBoundsH.get(), this.posInBoundsW.get());
Entity display = new Entity(EntityType.FALLING_BLOCK);
((FallingBlockMeta) display.getEntityMeta()).setBlock(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.getBoundingBox()
.grow(1, 1, 1)
.intersectBox(display.getPosition().sub(p.getPosition()), display.getBoundingBox())
)
.findAny()
.orElse(null);
if(player == null) return;
this.spawnPowerUp();
display.remove();
this.onPowerup(player);
});
}
private void onPowerup(Player 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));
}
}

View File

@@ -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<String, Option<?>> configuration) throws Exception {
return new SpaceSnake(configuration.get("width").getAsInt(), configuration.get("powerUpCount").getAsInt()).setParent(parent);
}
}

View File

@@ -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<String, Option<?>> configuration) {
return new Stickfight().setParent(parent);
return new Stickfight(configuration.get("length").getAsInt()).setParent(parent);
}
@Override

View File

@@ -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<Player, Pos> spawnPoints = new WeakHashMap<>();
private final Map<Player, Integer> 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<Void> 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<Player> 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;
}

View File

@@ -0,0 +1,254 @@
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;
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.entity.*;
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;
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;
class TurtleGame extends StatelessGame {
private final int radius;
private final Map<Player, Turtle> turtlePlayerMap = new WeakHashMap<>();
private final ArrayList<Entity> snacks = new ArrayList<>();
private final ArrayList<Entity> bombs = new ArrayList<>();
private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper");
private final double startSpeed;
public TurtleGame(int radius, int startSpeed) {
super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore());
this.radius = radius;
this.startSpeed = startSpeed;
this.eventNode()
.addListener(PlayerTickEvent.class, this::onPlayerTick)
.addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak)
.addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak);
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
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);
}
}
}
}
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;
Turtle turtle = this.turtlePlayerMap.get(p);
turtle.adaptView();
if(this.isRunning()) {
turtle.move();
this.snacks.stream()
.filter(turtle::checkCollisionWithEntity)
.toList()
.forEach(snack -> {
this.eat(p, snack);
this.generateNewSnack();
if(this.turtlePlayerMap.get(p).getScore() % 5 == 0) {
this.generateNewBomb();
this.addSpeed(0.4, p);
}
});
this.bombs.stream()
.filter(turtle::checkCollisionWithEntity)
.toList()
.forEach(bomb -> {
this.explode(p, bomb);
this.generateNewBombs(2);
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 = 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();
this.turtlePlayerMap.get(p).increaseScore();
this.turtlePlayerMap.get(p).increaseBoostChargeLevel(0.04f);
}
protected void explode(Player p, Entity bomb) {
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));
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<Player> getLeftPlayers() {
return this.turtlePlayerMap.keySet().stream()
.filter(player -> !this.getScore().hasResult(player))
.toList();
}
@Override
protected boolean onPlayerJoin(Player p) {
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) {
Turtle 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 generateNewSnacks(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();
meta.setBlock(this.snackBlock);
meta.setCustomName(Component.text("Snack"));
meta.setCustomNameVisible(true);
Pos spawnPosition = this.newSpawnPosition(snack);
if(spawnPosition == null) {
snack.remove();
return;
}
snack.setInstance(this, spawnPosition);
this.snacks.add(snack);
}
private void generateNewBombs(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();
meta.setBlock(Block.TNT);
meta.setCustomName(Component.text("Bomb").color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
meta.setCustomNameVisible(true);
Pos spawnPosition = this.newSpawnPosition(bomb, false);
if(spawnPosition == null) {
bomb.remove();
return;
}
bomb.setInstance(this, spawnPosition);
this.bombs.add(bomb);
}
private @Nullable Pos newSpawnPosition(Entity entity) {
return this.newSpawnPosition(entity, true);
}
private @Nullable Pos newSpawnPosition(Entity entity, boolean nearPlayers) {
Pos spawnPosition;
int counter = 0;
boolean isInRadius, collides;
do {
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);
Pos checkPosition = spawnPosition;
isInRadius = checkPosition.distance(0, 1, 0) < this.radius-2;
collides = this.getEntities().stream()
.filter(e -> !e.equals(entity))
.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;
}
@Override
protected void onStart() {
this.generateNewSnacks(this.turtlePlayerMap.size() + 1);
this.generateNewBombs((int) Math.ceil(this.snacks.size() * 0.5));
this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill);
}
@Override
protected void onStop() {
this.getLeftPlayers().forEach(this::letPlayerLoose);
}
}

View File

@@ -0,0 +1,42 @@
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.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 TurtleGameFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_TurtleGame#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_TurtleGame#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new 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, 3, 4, 6, 8));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new TurtleGame(configuration.get("radius").getAsInt(), configuration.get("startSpeed").getAsInt()).setParent(parent);
}
@Override
public Material symbol() {
return Material.TURTLE_EGG;
}
}

View File

@@ -0,0 +1,110 @@
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 double boostSpeedMultiplier = 1;
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.player.getPosition());
this.addPassenger(this.player);
}
public void startBoostRefill() {
this.boostRefillTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
if(this.boostChargeLevel >= 1f) return;
this.increaseBoostChargeLevel(0.02f);
}, TaskSchedule.seconds(1), TaskSchedule.seconds(1));
}
public void destroy() {
this.removePassenger(this.player);
this.remove();
if(this.boostRefillTask != null && this.boostRefillTask.isAlive()) this.boostRefillTask.cancel();
if(this.boostTask != null && this.boostTask.isAlive()) this.boostTask.cancel();
}
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()));
}
public void move() {
this.adaptView();
Vec direction = this.player.getPosition().direction();
Vec movementVector = direction.withY(0).normalize().mul(this.speed * this.boostSpeedMultiplier);
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.boostSpeedMultiplier = 3.5;
this.boostTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
if(this.boostChargeLevel <= 0f) {
this.cancelBoost();
return;
}
this.boostChargeLevel = Math.max(0f, this.boostChargeLevel - 0.025f);
this.player.setExp(this.boostChargeLevel);
}, TaskSchedule.millis(30), TaskSchedule.millis(30));
}
public void cancelBoost() {
if(this.boostTask == null || !this.boostTask.isAlive()) return;
this.boostTask.cancel();
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;
}
public int getScore() {
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);
}
}

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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;

View File

@@ -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;

View File

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

View File

@@ -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<Material> filter) {
List<Material> 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()));
}
}

View File

@@ -12,7 +12,7 @@ public enum BlockPallet {
STONE(new Block[] {Block.CHISELED_STONE_BRICKS, Block.STONE_BRICKS, Block.POLISHED_ANDESITE, Block.POLISHED_BLACKSTONE, Block.POLISHED_DIORITE}),
WINTER(new Block[] {Block.SNOW_BLOCK, Block.ICE, Block.PACKED_ICE, Block.BLUE_CONCRETE, Block.SEA_LANTERN}),
STREET(new Block[] {Block.BLACK_CONCRETE_POWDER, Block.GRAY_CONCRETE_POWDER, Block.GRAVEL, Block.BLACK_CONCRETE, Block.GRAY_CONCRETE}),
FLOWER(new Block[] {Block.ORANGE_TULIP, Block.PINK_TULIP, Block.RED_TULIP, Block.WHITE_TULIP}),
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<Block> list;

View File

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

View File

@@ -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
@@ -148,3 +161,12 @@ 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!
;;
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
;;
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
Can't render this file because it has a wrong number of fields in line 127.