14 Commits

Author SHA1 Message Date
460d737d3d reset player level 2025-07-19 02:21:23 +02:00
7591ce29ff added strong towers 2025-07-19 02:09:55 +02:00
39bbf19d9d reset more player values in Room 2025-07-19 01:48:35 +02:00
df239d8166 made armadillo faster and chicken slower 2025-07-19 01:27:35 +02:00
bf9c16ccc8 removed log 2025-07-19 01:24:40 +02:00
b5d6d7bac4 added wave mechanic 2025-07-19 01:10:57 +02:00
3b55f16b24 added range to cursor 2025-04-16 23:43:46 +02:00
5ef254b75f fixed tower spawning issues 2025-04-14 17:26:59 +02:00
5e5f36153e added own cursor class 2025-04-13 23:28:46 +02:00
e77879c657 added tower value calculation and delete tower mechanic 2025-04-13 18:35:50 +02:00
e3068be160 moved shooting mechanic to ShootingTower, added money 2025-04-13 16:12:08 +02:00
867bee1c5a corrected PlayerLoginHandler 2025-04-13 13:07:28 +02:00
893923cc56 added towers and added tower spawn mechanics 2025-04-12 23:48:06 +02:00
38c944e6c1 added basic towerdefense logic 2025-04-12 19:22:37 +02:00
69 changed files with 1094 additions and 1836 deletions

View File

@@ -48,7 +48,7 @@ dependencies {
//Tools //Tools
implementation 'de.articdive:jnoise:3.0.2' implementation 'de.articdive:jnoise:3.0.2'
implementation 'net.md-5:bungeecord-config:1.21-R0.3' implementation 'net.md-5:bungeecord-config:1.19-R0.1-SNAPSHOT'
implementation 'org.apache.commons:commons-text:1.10.0' implementation 'org.apache.commons:commons-text:1.10.0'
implementation 'org.spongepowered:configurate-yaml:4.1.2' implementation 'org.spongepowered:configurate-yaml:4.1.2'
implementation 'com.sparkjava:spark-core:2.9.4' implementation 'com.sparkjava:spark-core:2.9.4'

View File

@@ -21,6 +21,7 @@ public enum Commands {
ROOM(new RoomCommand()), ROOM(new RoomCommand()),
UPDATE(new RefreshCommandsCommand()), UPDATE(new RefreshCommandsCommand()),
OP(new OpCommand()), OP(new OpCommand()),
FAKEPLAYER(new FakeplayerCommand()),
KICK(new KickCommand()), KICK(new KickCommand()),
SKIN(new SkinCommand()), SKIN(new SkinCommand()),
SETOWNER(new SetRoomOwnerCommand()), SETOWNER(new SetRoomOwnerCommand()),
@@ -40,11 +41,7 @@ public enum Commands {
static { static {
MinecraftServer.getCommandManager().setUnknownCommandCallback((sender, command) -> { MinecraftServer.getCommandManager().setUnknownCommandCallback((sender, command) -> {
if(command.isBlank()) return; if(command.isBlank()) return;
new ChatMessage(Icon.ERROR) new ChatMessage(Icon.ERROR).appendStatic("Unknown command: ").quote(command).send(sender);
.appendTranslated("common#unknownCommand")
.appendSpace()
.quote(command)
.send(sender);
}); });
} }
} }

View File

@@ -0,0 +1,31 @@
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) -> { setDefaultExecutor((sender, context) -> {
if(sender instanceof Player p) { if(sender instanceof Player p) {
Room.getRoom(p).orElseThrow().setOwner(p); Room.getRoom(p).orElseThrow().setOwner(p);
new ChatMessage(Icon.SUCCESS).appendTranslated("room#ownerSelf").send(sender); new ChatMessage(Icon.SUCCESS).appendStatic("You are now the owner of this room!").send(sender);
} }
}); });
@@ -29,7 +29,7 @@ public class SetRoomOwnerCommand extends PrivilegedCommand {
if(sender instanceof Player p) { if(sender instanceof Player p) {
Player newOwner = MinecraftServer.getConnectionManager().getOnlinePlayerByUsername(context.getRaw("player")); Player newOwner = MinecraftServer.getConnectionManager().getOnlinePlayerByUsername(context.getRaw("player"));
Room.getRoom(p).orElseThrow().setOwner(Objects.requireNonNull(newOwner)); Room.getRoom(p).orElseThrow().setOwner(Objects.requireNonNull(newOwner));
new ChatMessage(Icon.SUCCESS).appendTranslated("room#ownerSet").send(sender); new ChatMessage(Icon.SUCCESS).appendStatic("The new owner has been set!").send(sender);
} }
}, ArgumentType.Entity("player").onlyPlayers(true)); }, ArgumentType.Entity("player").onlyPlayers(true));
} }

View File

@@ -7,8 +7,7 @@ import net.minestom.server.event.EventListener;
public enum Listeners { public enum Listeners {
SPAWN(new AddEntityToInstanceEventListener()), SPAWN(new AddEntityToInstanceEventListener()),
LOGIN(new PlayerLoginHandler()), LOGIN(new PlayerLoginHandler()),
LEAVE(new PlayerLeaveHandler()), LEAVE(new PlayerLeaveHandler());
CHAT(new ChatFormatHandler());
Listeners(EventListener<?> event) { Listeners(EventListener<?> event) {
MinecraftServer.getGlobalEventHandler().addListener(event); MinecraftServer.getGlobalEventHandler().addListener(event);

View File

@@ -1,20 +0,0 @@
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,9 +1,5 @@
package eu.mhsl.minenet.minigames.handler.global; 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.entity.Player;
import net.minestom.server.event.EventListener; import net.minestom.server.event.EventListener;
import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerDisconnectEvent;
@@ -18,11 +14,7 @@ public class PlayerLeaveHandler implements EventListener<PlayerDisconnectEvent>
@Override @Override
public @NotNull Result run(@NotNull PlayerDisconnectEvent event) { public @NotNull Result run(@NotNull PlayerDisconnectEvent event) {
Player p = event.getPlayer(); Player p = event.getPlayer();
new ChatMessage(Icon.LEAVE) // new ChatMessage(Icon.SCIENCE).appendStatic("unübersetzter Leavetext: ").appendStatic(p.getDisplayName()).send(MinecraftServer.getConnectionManager().getOnlinePlayers());
.appendStatic(p.getName().color(NamedTextColor.GRAY))
.appendSpace()
.appendTranslated("common#leave", NamedTextColor.DARK_GRAY)
.send(MinecraftServer.getConnectionManager().getOnlinePlayers());
return Result.SUCCESS; return Result.SUCCESS;
} }
} }

View File

@@ -65,6 +65,8 @@ public class PlayerLoginHandler implements EventListener<AsyncPlayerConfiguratio
Logger.getLogger("user").info(p.getUsername() + " joined"); 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; return Result.SUCCESS;
} }
} }

View File

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

View File

@@ -2,26 +2,20 @@ package eu.mhsl.minenet.minigames.instance.game;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.anvilRun.AnvilRunFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.anvilRun.AnvilRunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace.BlockBreakRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.bowSpleef.BowSpleefFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.bowSpleef.BowSpleefFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.elytraRace.ElytraRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.elytraRace.ElytraRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.BackroomsFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.BackroomsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain.AcidRainFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain.AcidRainFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge.FastbridgeFactory;
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.jumpDive.JumpDiveFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.minerun.MinerunFactory; 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.spleef.SpleefFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight.StickFightFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight.StickFightFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.TetrisFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.TetrisFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.sumo.SumoFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tntrun.TntRunFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.tntrun.TntRunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseFactory; 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.trafficlightrace.TrafficLightRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.TurtleGameFactory;
public enum GameList { public enum GameList {
DEATHCUBE(new DeathcubeFactory(), GameType.JUMPNRUN), DEATHCUBE(new DeathcubeFactory(), GameType.JUMPNRUN),
@@ -36,15 +30,9 @@ public enum GameList {
TNTRUN(new TntRunFactory(), GameType.OTHER), TNTRUN(new TntRunFactory(), GameType.OTHER),
ANVILRUN(new AnvilRunFactory(), GameType.PVE), ANVILRUN(new AnvilRunFactory(), GameType.PVE),
ACIDRAIN(new AcidRainFactory(), GameType.PVE), ACIDRAIN(new AcidRainFactory(), GameType.PVE),
TURTLEGAME(new TurtleGameFactory(), GameType.PVE),
ELYTRARACE(new ElytraRaceFactory(), GameType.PVP), ELYTRARACE(new ElytraRaceFactory(), GameType.PVP),
SPLEEF(new SpleefFactory(), GameType.PVP), SPLEEF(new SpleefFactory(), GameType.PVP),
JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN), JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN);
SUMO(new SumoFactory(), GameType.PVP),
HIGHGROUND(new HighGroundFactory(), GameType.PVP),
FASTBRIDGE(new FastbridgeFactory(), GameType.OTHER),
BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER),
SPACESNAKE(new SpaceSnakeFactory(), GameType.PVP);
private final GameFactory factory; private final GameFactory factory;
private final GameType type; private final GameType type;
@@ -57,6 +45,6 @@ public enum GameList {
} }
public GameType getType() { public GameType getType() {
return this.type; return type;
} }
} }

View File

@@ -57,11 +57,7 @@ public class StatelessGame extends Game {
int timeLeft = timeLimit - timePlayed; int timeLeft = timeLimit - timePlayed;
switch (timeLeft) { switch (timeLeft) {
case 90, 60, 30, 10, 5, 4, 3, 2, 1 -> case 90, 60, 30, 10, 5, 4, 3, 2, 1 ->
new ChatMessage(Icon.TIME) new ChatMessage(Icon.SCIENCE).appendStatic("Noch " + timeLeft + " Sekunden!").send(getPlayers());
.appendStatic(String.valueOf(timeLeft))
.appendSpace()
.appendTranslated(timeLeft == 1 ? "common#secondsLeft_singular" : "common#secondsLeft_plural")
.send(getPlayers());
} }
timePlayed++; timePlayed++;
@@ -81,24 +77,14 @@ public class StatelessGame extends Game {
* When overriding make sure to call this::start after countdown! * When overriding make sure to call this::start after countdown!
*/ */
protected CompletableFuture<Void> countdownStart() { protected CompletableFuture<Void> countdownStart() {
Duration fadeIn = Duration.ofMillis(300);
Duration stay = Duration.ofMillis(700);
return new Countdown(TitleMessage.class) return new Countdown(TitleMessage.class)
.countdown( .countdown(Audience.audience(getPlayers()), 5, countdownModifier -> countdownModifier.message = new TitleMessage(Duration.ofMillis(300), Duration.ofMillis(700))
Audience.audience(this.getPlayers()), .subtitle(subtitleMessage -> subtitleMessage.appendStatic(Component.text("in ", NamedTextColor.DARK_GREEN))
5, .appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN))
modifier -> modifier.message = new TitleMessage(fadeIn, stay) .appendStatic(Component.text(" seconds", NamedTextColor.DARK_GREEN))));
.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() { public void startAccessor() {
this.start(); this.start();
} }

View File

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

View File

@@ -1,79 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.FirstWinsScore;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class BlockBreakRace extends StatelessGame {
private int spawnCount = 0;
private final int height;
public BlockBreakRace(int height) {
super(Dimension.OVERWORLD.key, "blockBreakRace", new FirstWinsScore());
this.height = height;
this.setGenerator(new BlockBreakRaceGenerator(height));
}
@Override
protected boolean onPlayerJoin(Player p) {
PlayerInventory inv = p.getInventory();
inv.addItemStack(ItemStack.of(Material.DIAMOND_PICKAXE));
inv.addItemStack(ItemStack.of(Material.DIAMOND_AXE));
inv.addItemStack(ItemStack.of(Material.DIAMOND_SHOVEL));
return super.onPlayerJoin(p);
}
@Override
protected void onStart() {
this.getPlayers().forEach(player -> player.setGameMode(GameMode.SURVIVAL));
}
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
List<Material> allowedMaterials = List.of(Material.STONE, Material.OAK_PLANKS, Material.DIRT);
if(!allowedMaterials.contains(event.getBlock().registry().material())) event.setCancelled(true);
if(this.isBeforeBeginning) event.setCancelled(true);
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
if(this.isBeforeBeginning) return;
if(playerMoveEvent.getNewPosition().y() < BlockBreakRaceGenerator.BOTTOM_Y) {
Player player = playerMoveEvent.getPlayer();
this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR);
player.getInventory().clear();
}
}
@Override
public Pos getSpawn() {
int idx = this.spawnCount++;
int cols = BlockBreakRaceGenerator.ROW_OFFSETS_X.length;
int rows = BlockBreakRaceGenerator.ROW_OFFSETS_Z.length;
int perChunk = cols * rows;
int zChunk = idx / perChunk;
int gridIndex = idx % perChunk;
int xIndex = gridIndex % cols;
int zIndex = gridIndex / cols;
int localX = BlockBreakRaceGenerator.ROW_OFFSETS_X[xIndex];
int localZ = BlockBreakRaceGenerator.ROW_OFFSETS_Z[zIndex];
int absZ = (zChunk * 16) + localZ;
return new Pos(localX, BlockBreakRaceGenerator.BOTTOM_Y + this.height + 1, absZ).add(0.5);
}
}

View File

@@ -1,40 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace;
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 BlockBreakRaceFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_BlockBreakRace#name");
}
@Override
public Material symbol() {
return Material.DIAMOND_PICKAXE;
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_BlockBreakRace#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("height", Material.SCAFFOLDING, TranslatedComponent.byId("optionCommon#height"), 20, 30, 40, 50));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new BlockBreakRace(configuration.get("height").getAsInt()).setParent(parent);
}
}

View File

@@ -1,67 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace;
import eu.mhsl.minenet.minigames.world.generator.featureEnriched.ValeGenerator;
import eu.mhsl.minenet.minigames.world.generator.terrain.BaseGenerator;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class BlockBreakRaceGenerator extends BaseGenerator {
public static final int BOTTOM_Y = 50;
public final int TOP_Y;
public static final int[] ROW_OFFSETS_X = {4, 8, 12};
public static final int[] ROW_OFFSETS_Z = {4, 8, 12};
private static final Block[] FILL_BLOCKS = {
Block.STONE,
Block.DIRT,
Block.OAK_PLANKS
};
private final Random random = ThreadLocalRandom.current();
public BlockBreakRaceGenerator(int height) {
this.TOP_Y = BOTTOM_Y + height;
ValeGenerator vale = new ValeGenerator();
vale.setXShiftMultiplier(i -> 0.5d);
vale.setHeightNoiseMultiplier(i -> 2);
vale.setXShiftOffset(i -> 40d);
this.addMixIn(vale);
this.addMixIn(unit -> {
if(unit.absoluteStart().chunkX() != 0) return;
for (int localX : ROW_OFFSETS_X) {
for (int localZ : ROW_OFFSETS_Z) {
final int absZ = unit.absoluteStart().blockZ() + localZ;
this.placeTube(unit, localX, absZ);
}
}
});
}
private void placeTube(GenerationUnit unit, int x, int z) {
for (int y = BOTTOM_Y; y < this.TOP_Y; y++) {
Block fill = FILL_BLOCKS[this.random.nextInt(FILL_BLOCKS.length)];
unit.modifier().fill(
new Pos(x, y, z),
new Pos(x, y, z).add(1),
fill
);
}
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dz == 0) continue; // Zentrum überspringen
unit.modifier().fill(
new Pos(x + dx, BOTTOM_Y, z + dz),
new Pos(x + dx, this.TOP_Y + 3, z + dz).add(1),
Block.BARRIER
);
}
}
}
}

View File

@@ -3,8 +3,6 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.bowSpleef;
import eu.mhsl.minenet.minigames.instance.game.Game; 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.GameFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option; import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.restriction.RestrictionHandler;
import eu.mhsl.minenet.minigames.instance.game.stateless.config.restriction.common.MinimalPlayeramountGameRestriction;
import eu.mhsl.minenet.minigames.instance.room.Room; import eu.mhsl.minenet.minigames.instance.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
@@ -31,10 +29,4 @@ public class BowSpleefFactory implements GameFactory {
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception { public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new BowSpleef().setParent(parent); return new BowSpleef().setParent(parent);
} }
@Override
public RestrictionHandler globalRestrictions() {
return new RestrictionHandler()
.addRestriction(new MinimalPlayeramountGameRestriction(2));
}
} }

View File

@@ -8,7 +8,6 @@ import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import io.github.togar2.pvp.feature.CombatFeatures; import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -69,9 +68,7 @@ class Deathcube extends StatelessGame {
playerMoveEvent.setCancelled(true); playerMoveEvent.setCancelled(true);
return; return;
} }
if(playerMoveEvent.getNewPosition().y() <= height) return; if(playerMoveEvent.getNewPosition().y() > height) getScore().insertResult(playerMoveEvent.getPlayer());
getScore().insertResult(playerMoveEvent.getPlayer());
playerMoveEvent.getPlayer().setGameMode(GameMode.SPECTATOR);
} }
@Override @Override

View File

@@ -20,10 +20,7 @@ import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EquipmentSlot; import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.entity.GameMode; import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.*;
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.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
@@ -36,7 +33,6 @@ import org.jetbrains.annotations.NotNull;
import java.time.Duration; import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -52,74 +48,80 @@ public class ElytraRace extends StatelessGame {
private final Material resetMaterial = Material.RED_DYE; private final Material resetMaterial = Material.RED_DYE;
private final int boostMultiplier = 50; private final int boostMultiplier = 50;
private final Material ringMaterial = Material.GOLD_BLOCK; private final Material ringMaterial = Material.GOLD_BLOCK;
private final Map<Player, CheckPointData> playerCheckpoints = new HashMap<>();
private int generatedUntil = 0; 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<>();
public ElytraRace(int ringCount) { public ElytraRace(int ringCount) {
super(Dimension.OVERWORLD.key, "ElytraRace", new FirstWinsScore()); super(Dimension.OVERWORLD.key, "ElytraRace", new FirstWinsScore());
this.ringCount = ringCount; this.ringCount = ringCount;
this.setGenerator(this.vale); setGenerator(vale);
this.vale.setCalculateSeaLevel(point -> this.seaLevel); vale.setCalculateSeaLevel(point -> seaLevel);
this.vale.setXShiftMultiplier(integer -> NumberUtil.map(integer, 50, 500, 0, 1)); vale.setXShiftMultiplier(integer -> NumberUtil.map(integer, 50, 500, 0, 1));
this.vale.addMixIn(new PlaneTerrainGenerator(this.gameHeight, Block.BARRIER)); vale.addMixIn(new PlaneTerrainGenerator(gameHeight, Block.BARRIER));
this.eventNode().addListener(PlayerUseItemEvent.class, playerUseItemEvent -> { eventNode().addListener(PlayerUseItemEvent.class, playerUseItemEvent -> {
Player player = playerUseItemEvent.getPlayer(); Player player = playerUseItemEvent.getPlayer();
Material usedMaterial = playerUseItemEvent.getItemStack().material(); Material usedMaterial = playerUseItemEvent.getItemStack().material();
if (usedMaterial.equals(this.boostMaterial)) { if(usedMaterial.equals(boostMaterial)) {
if(!player.isFlyingWithElytra()) return; if(!player.isFlyingWithElytra()) return;
this.boost(player); boost(player);
InventoryUtil.removeItemFromPlayer(player, this.boostMaterial, 1); InventoryUtil.removeItemFromPlayer(player, boostMaterial, 1);
} else if (usedMaterial.equals(this.resetMaterial)) { } else if(usedMaterial.equals(resetMaterial)) {
this.toCheckpoint(player); toCheckpoint(player);
InventoryUtil.removeItemFromPlayer(player, this.resetMaterial, 1); InventoryUtil.removeItemFromPlayer(player, resetMaterial, 1);
} }
}); });
this.eventNode().addListener(PlayerStopFlyingWithElytraEvent.class, playerStopFlyingWithElytraEvent -> { eventNode().addListener(PlayerStopFlyingWithElytraEvent.class, playerStopFlyingWithElytraEvent -> {
Player player = playerStopFlyingWithElytraEvent.getPlayer(); Player player = playerStopFlyingWithElytraEvent.getPlayer();
if (Position.blocksBelowPlayer(this, player).contains(this.ringMaterial.block())) { if(Position.blocksBelowPlayer(this, player).contains(ringMaterial.block())) {
player.setFlyingWithElytra(true); player.setFlyingWithElytra(true);
this.boost(player); boost(player);
} else { } else {
this.toCheckpoint(player); toCheckpoint(player);
// getScore().insertResult(playerStopFlyingWithElytraEvent.getPlayer()); // getScore().insertResult(playerStopFlyingWithElytraEvent.getPlayer());
// playerStopFlyingWithElytraEvent.getPlayer().setGameMode(GameMode.SPECTATOR); // playerStopFlyingWithElytraEvent.getPlayer().setGameMode(GameMode.SPECTATOR);
} }
}); });
this.eventNode().addListener(PlayerStartFlyingWithElytraEvent.class, playerStartFlyingWithElytraEvent -> { eventNode().addListener(PlayerStartFlyingWithElytraEvent.class, playerStartFlyingWithElytraEvent -> {
if (!this.isRunning) { if(!isRunning) {
playerStartFlyingWithElytraEvent.getPlayer().setFlyingWithElytra(false); playerStartFlyingWithElytraEvent.getPlayer().setFlyingWithElytra(false);
return; return;
} }
this.boost(playerStartFlyingWithElytraEvent.getPlayer()); boost(playerStartFlyingWithElytraEvent.getPlayer());
}); });
} }
@Override @Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) { protected void onLoad(@NotNull CompletableFuture<Void> callback) {
Point spawnpoint = new Pos(this.vale.getXShiftAtZ(0), -46, 0); Point spawnpoint = new Pos(vale.getXShiftAtZ(0), -46, 0);
GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> this.setBlock(point, BlockPallet.STREET.rnd())); GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> setBlock(point, BlockPallet.STREET.rnd()));
this.generateRing(this.ringSpacing); generateRing(ringSpacing);
this.generateRing(this.ringSpacing * 2); generateRing(ringSpacing * 2);
callback.complete(null); callback.complete(null);
} }
@Override @Override
protected void onStart() { protected void onStart() {
this.getPlayers().forEach(player -> { getPlayers().forEach(player -> {
player.getInventory().setEquipment(EquipmentSlot.CHESTPLATE, (byte) 0, ItemStack.of(Material.ELYTRA)); player.getInventory().setEquipment(EquipmentSlot.CHESTPLATE, (byte) 0, ItemStack.of(Material.ELYTRA));
for(int i = 0; i < 3; i++) { for(int i = 0; i < 3; i++) {
player.getInventory().setItemStack(i, ItemStack.builder(this.boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build()); player.getInventory().setItemStack(i, ItemStack.builder(boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build());
} }
this.addResetItemToPlayer(player); addResetItemToPlayer(player);
}); });
} }
@@ -128,24 +130,24 @@ public class ElytraRace extends StatelessGame {
Player player = playerMoveEvent.getPlayer(); Player player = playerMoveEvent.getPlayer();
Point newPos = playerMoveEvent.getNewPosition(); Point newPos = playerMoveEvent.getNewPosition();
if (this.isBeforeBeginning && playerMoveEvent.getNewPosition().y() < this.getSpawn().y()) { if(isBeforeBeginning && playerMoveEvent.getNewPosition().y() < getSpawn().y()) {
player.teleport(this.getSpawn()); player.teleport(getSpawn());
return; return;
} }
this.playerCheckpoints.putIfAbsent(player, new CheckPointData(this.ringSpacing, this.ringSpacing * 2)); playerCheckpoints.putIfAbsent(player, new CheckPointData(ringSpacing, ringSpacing * 2));
if (newPos.z() > this.generatedUntil - this.ringSpacing) { if(newPos.z() > generatedUntil - ringSpacing) {
this.generateRing(this.generatedUntil + this.ringSpacing); generateRing(generatedUntil + ringSpacing);
} }
if (newPos.z() > this.playerCheckpoints.get(player).nextCheckpoint) { if(newPos.z() > playerCheckpoints.get(player).nextCheckpoint) {
this.playerCheckpoints.put(player, this.playerCheckpoints.get(player).next(this.ringSpacing)); playerCheckpoints.put(player, playerCheckpoints.get(player).next(ringSpacing));
this.boost(player); boost(player);
} }
if (newPos.y() > this.gameHeight - 5) { if(newPos.y() > gameHeight - 5) {
Point particlePoint = newPos.withY(this.gameHeight); Point particlePoint = newPos.withY(gameHeight);
ParticlePacket particle = new ParticlePacket( ParticlePacket particle = new ParticlePacket(
Particle.WAX_ON, Particle.WAX_ON,
particlePoint.blockX(), particlePoint.blockX(),
@@ -155,17 +157,17 @@ public class ElytraRace extends StatelessGame {
0, 0,
30, 30,
1f, 1f,
Math.toIntExact((long) NumberUtil.map(newPos.y(), this.gameHeight - 5, this.gameHeight, 50, 500)) Math.toIntExact((long) NumberUtil.map(newPos.y(), gameHeight - 5, gameHeight, 50, 500))
); );
player.sendPacket(particle); player.sendPacket(particle);
} }
if (this.getBlock(player.getPosition()).equals(Block.WATER)) { if(getBlock(player.getPosition()).equals(Block.WATER)) {
this.toCheckpoint(player); toCheckpoint(player);
} }
if (newPos.z() > this.ringCount * this.ringSpacing) { if(newPos.z() > ringCount * ringSpacing) {
this.getScore().insertResult(player); getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR); player.setGameMode(GameMode.SPECTATOR);
player.setFlyingWithElytra(false); player.setFlyingWithElytra(false);
} }
@@ -173,35 +175,34 @@ public class ElytraRace extends StatelessGame {
@Override @Override
public Pos getSpawn() { public Pos getSpawn() {
return new Pos(this.vale.getXShiftAtZ(0), -45, 0); return new Pos(vale.getXShiftAtZ(0), -45, 0);
} }
private void addResetItemToPlayer(Player p) { private void addResetItemToPlayer(Player p) {
p.getInventory().setItemStack(8, ItemStack.builder(this.resetMaterial).customName(TranslatedComponent.byId("reset").getAssembled(p)).build()); p.getInventory().setItemStack(8, ItemStack.builder(resetMaterial).customName(TranslatedComponent.byId("reset").getAssembled(p)).build());
} }
private Point getRingPositionAtZ(int z) { private Point getRingPositionAtZ(int z) {
Random random = new Random(this.hashCode() + z); return new Pos(vale.getXShiftAtZ(z), -45, z);
return new Pos(this.vale.getXShiftAtZ(z), -45 + random.nextInt(-5, 15), z);
} }
private CompletableFuture<Void> generateRing(int zPos) { private CompletableFuture<Void> generateRing(int zPos) {
if (zPos > this.ringCount * this.ringSpacing) return null; if(zPos > ringCount * ringSpacing) return null;
boolean isLast = (zPos == this.ringCount * this.ringSpacing); boolean isLast = (zPos == ringCount * ringSpacing);
this.generatedUntil = zPos; generatedUntil = zPos;
AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
Point ringPos = this.getRingPositionAtZ(zPos); Point ringPos = getRingPositionAtZ(zPos);
GeneratorUtils.iterateArea( GeneratorUtils.iterateArea(
ringPos.sub(100, 0, 0).withY(0), ringPos.sub(100, 0, 0).withY(0),
ringPos.add(100, 0, 0).withY(this.seaLevel), ringPos.add(100, 0, 0).withY(seaLevel),
point -> batch.setBlock(point, Block.BARRIER) point -> batch.setBlock(point, Block.BARRIER)
); );
GeneratorUtils.iterateArea( GeneratorUtils.iterateArea(
ringPos.sub(3, 3, 0), ringPos.sub(3, 3, 0),
ringPos.add(3, 3, 0), ringPos.add(3, 3, 0),
point -> batch.setBlock(point, isLast ? Block.DIAMOND_BLOCK : this.ringMaterial.block()) point -> batch.setBlock(point, isLast ? Block.DIAMOND_BLOCK : ringMaterial.block())
); );
GeneratorUtils.iterateArea( GeneratorUtils.iterateArea(
ringPos.sub(2, 2, 0), ringPos.sub(2, 2, 0),
@@ -209,8 +210,7 @@ public class ElytraRace extends StatelessGame {
point -> batch.setBlock(point, Block.AIR) point -> batch.setBlock(point, Block.AIR)
); );
BatchUtil.loadAndApplyBatch(batch, this, () -> { BatchUtil.loadAndApplyBatch(batch, this, () -> {});
});
return null; return null;
} }
@@ -219,13 +219,13 @@ public class ElytraRace extends StatelessGame {
Vec playerVelocity = player.getPosition().direction(); Vec playerVelocity = player.getPosition().direction();
player.setVelocity( player.setVelocity(
player.getVelocity().add(playerVelocity.mul(this.boostMultiplier)) player.getVelocity().add(playerVelocity.mul(boostMultiplier))
.withY(playerVelocity.withY(v -> v * this.boostMultiplier).y()) .withY(playerVelocity.withY(v -> v * boostMultiplier).y())
); );
} }
private void toCheckpoint(Player p) { private void toCheckpoint(Player p) {
Point checkpointPos = this.getRingPositionAtZ(this.playerCheckpoints.get(p).currentCheckpoint); Point checkpointPos = getRingPositionAtZ(playerCheckpoints.get(p).currentCheckpoint);
p.setVelocity(Vec.ZERO); p.setVelocity(Vec.ZERO);
p.setFlyingWithElytra(false); p.setFlyingWithElytra(false);
p.teleport(Pos.fromPoint(checkpointPos).add(0.5, 0, 0.5)); p.teleport(Pos.fromPoint(checkpointPos).add(0.5, 0, 0.5));
@@ -245,24 +245,16 @@ public class ElytraRace extends StatelessGame {
.subtitle( .subtitle(
subtitleMessage -> subtitleMessage ->
subtitleMessage subtitleMessage
.appendTranslated("game_ElytraRace#launchIn") .appendStatic(Component.text("Launch in ", NamedTextColor.DARK_GREEN))
.appendSpace()
.appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN)) .appendStatic(Component.text(countdownModifier.timeLeft, NamedTextColor.GREEN))
.appendSpace() .appendStatic(Component.text(" seconds", NamedTextColor.DARK_GREEN))
.appendTranslated(countdownModifier.timeLeft == 1 ? "common#second" : "common#seconds")
) )
).thenRun(() -> { ).thenRun(() -> {
p.setFlying(false); p.setFlying(false);
p.setFlyingSpeed(1); p.setFlyingSpeed(1);
p.setFlyingWithElytra(true); p.setFlyingWithElytra(true);
this.boost(p); boost(p);
this.addResetItemToPlayer(p); addResetItemToPlayer(p);
}); });
} }
private record CheckPointData(int currentCheckpoint, int nextCheckpoint) {
public CheckPointData next(int spacing) {
return new CheckPointData(this.nextCheckpoint, this.nextCheckpoint + spacing);
}
}
} }

View File

@@ -1,64 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.FirstWinsScore;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull;
public class Fastbridge extends StatelessGame {
private int currentSpawn = 0;
public Fastbridge() {
super(Dimension.OVERWORLD.key, "Fastbridge", new FirstWinsScore());
this.setGenerator(new FastbridgeChunkgenerator());
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
Player player = playerMoveEvent.getPlayer();
Pos newPos = playerMoveEvent.getNewPosition();
if(this.getScore().hasResult(player)) return;
if(newPos.y() < 0) {
player.teleport(getSpawn());
if(!isBeforeBeginning) this.resetPlayer(player);
}
if(newPos.x() > 53) {
this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR);
}
}
@Override
protected void onStart() {
getPlayers().forEach(player -> {
player.setGameMode(GameMode.SURVIVAL);
resetPlayer(player);
});
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent playerBlockPlaceEvent) {
if(isBeforeBeginning) playerBlockPlaceEvent.setCancelled(true);
}
private void resetPlayer(Player player) {
if(isBeforeBeginning) return;
PlayerInventory inventory = player.getInventory();
inventory.clear();
inventory.addItemStack(ItemStack.of(Material.WHITE_WOOL, 64));
}
@Override
public synchronized Pos getSpawn() {
return new Pos(24, 1, currentSpawn++*Chunk.CHUNK_SIZE_Z*2-8, -90, 0);
}
}

View File

@@ -1,29 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge;
import eu.mhsl.minenet.minigames.world.generator.featureEnriched.ValeGenerator;
import eu.mhsl.minenet.minigames.world.generator.terrain.BaseGenerator;
import net.minestom.server.instance.block.Block;
public class FastbridgeChunkgenerator extends BaseGenerator {
public FastbridgeChunkgenerator() {
this.addMixIn(unit -> {
if (unit.absoluteStart().chunkZ() % 2 == 0) {
unit.modifier().fill(Block.BARRIER);
return;
}
if (unit.absoluteStart().chunkX() != 1 && unit.absoluteStart().chunkX() != 3) return;
for (int x = 5; x <= 10; x++){
for (int z = 5; z <= 10; z++){
unit.modifier().setRelative(x, 64, z, unit.absoluteStart().chunkX() == 3 ? Block.GOLD_BLOCK : Block.GRASS_BLOCK);
}
}
});
ValeGenerator vale = new ValeGenerator();
vale.setXShiftMultiplier(integer -> 0.5d);
vale.setHeightNoiseMultiplier(integer -> 2);
vale.setXShiftOffset(integer -> 40d);
this.addMixIn(vale);
}
}

View File

@@ -1,33 +0,0 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge;
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 net.minestom.server.item.Material;
import java.util.Map;
public class FastbridgeFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_Fastbridge#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_Fastbridge#description");
}
@Override
public Material symbol() {
return Material.WHITE_WOOL;
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new Fastbridge().setParent(parent);
}
}

View File

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

View File

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

View File

@@ -1,182 +0,0 @@
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

@@ -1,41 +0,0 @@
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

@@ -9,7 +9,6 @@ import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode; import net.minestom.server.entity.GameMode;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerStartDiggingEvent; import net.minestom.server.event.player.PlayerStartDiggingEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.batch.AbsoluteBlockBatch;
@@ -78,15 +77,6 @@ public class Spleef extends StatelessGame {
} }
} }
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
if(!isRunning) {
event.setCancelled(true);
return;
}
setBlock(event.getBlockPosition(), Block.AIR);
}
private void destroyBlock(PlayerStartDiggingEvent event) { private void destroyBlock(PlayerStartDiggingEvent event) {
if(!isRunning) return; if(!isRunning) return;
setBlock(event.getBlockPosition(), Block.AIR); setBlock(event.getBlockPosition(), Block.AIR);

View File

@@ -28,7 +28,7 @@ public class StickFightFactory implements GameFactory {
@Override @Override
public ConfigManager configuration() { public ConfigManager configuration() {
return new ConfigManager() return new ConfigManager()
.addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 7, 10, 13, 16, 19)); .addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 5, 7, 9, 11));
} }
@Override @Override
@@ -40,7 +40,7 @@ public class StickFightFactory implements GameFactory {
@Override @Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) { public Game manufacture(Room parent, Map<String, Option<?>> configuration) {
return new Stickfight(configuration.get("length").getAsInt()).setParent(parent); return new Stickfight().setParent(parent);
} }
@Override @Override

View File

@@ -2,7 +2,7 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LowestPointsWinScore; import eu.mhsl.minenet.minigames.score.LastWinsScore;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import io.github.togar2.pvp.events.FinalAttackEvent; import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures; import io.github.togar2.pvp.feature.CombatFeatures;
@@ -13,19 +13,15 @@ import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class Stickfight extends StatelessGame { public class Stickfight extends StatelessGame {
private final double radius; private final double radius = 20;
private final WeakHashMap<Player, Pos> spawnPoints = new WeakHashMap<>(); private final WeakHashMap<Player, Pos> spawnPoints = new WeakHashMap<>();
private final Map<Player, Integer> scoreMap = new WeakHashMap<>();
private boolean countdownStarted = false;
public Stickfight(int length) { public Stickfight() {
super(Dimension.OVERWORLD.key, "Stickfight", new LowestPointsWinScore()); super(Dimension.OVERWORLD.key, "Stickfight", new LastWinsScore());
this.radius = length;
eventNode().addChild( eventNode().addChild(
CombatFeatures.empty() CombatFeatures.empty()
@@ -36,7 +32,6 @@ public class Stickfight extends StatelessGame {
); );
eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> { eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> {
if(isBeforeBeginning) finalAttackEvent.setCancelled(true);
finalAttackEvent.setBaseDamage(0); finalAttackEvent.setBaseDamage(0);
((Player) finalAttackEvent.getTarget()).setHealth(20); ((Player) finalAttackEvent.getTarget()).setHealth(20);
}); });
@@ -46,26 +41,14 @@ public class Stickfight extends StatelessGame {
@Override @Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) { protected void onLoad(@NotNull CompletableFuture<Void> callback) {
this.replaceCircle(Block.SANDSTONE); setBlock(0, 50, 0, Block.DIAMOND_BLOCK);
}
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 @Override
protected void start() { protected void start() {
List<Player> players = getPlayers().stream().toList(); List<Player> players = getPlayers().stream().toList();
int numPlayers = players.size(); int numPlayers = players.size();
this.countdownStarted = true;
this.replaceCircle(Block.AIR);
for (int i = 0; i < numPlayers; i++) { for (int i = 0; i < numPlayers; i++) {
double angle = (2 * Math.PI / numPlayers) * i; double angle = (2 * Math.PI / numPlayers) * i;
int spawnX = (int) (radius * Math.cos(angle)); int spawnX = (int) (radius * Math.cos(angle));
@@ -83,11 +66,6 @@ public class Stickfight extends StatelessGame {
super.start(); super.start();
} }
@Override
protected void onStop() {
this.scoreMap.forEach((player, score) -> getScore().insertResult(player, score));
}
private void generateBridge(int startX, int startY, int startZ) { private void generateBridge(int startX, int startY, int startZ) {
int steps = (int) (radius * 1.5); int steps = (int) (radius * 1.5);
for (int i = 0; i < steps; i++) { for (int i = 0; i < steps; i++) {
@@ -100,23 +78,19 @@ public class Stickfight extends StatelessGame {
@Override @Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
Player player = playerMoveEvent.getPlayer(); if(!spawnPoints.containsKey(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; return;
} }
if(isBeforeBeginning) { if(isBeforeBeginning) {
if(spawnPoints.get(player).distance(playerMoveEvent.getNewPosition()) < 1) return; if(spawnPoints.get(playerMoveEvent.getPlayer()).distance(playerMoveEvent.getNewPosition()) < 1) return;
playerMoveEvent.setCancelled(true); playerMoveEvent.setCancelled(true);
player.teleport(spawnPoints.get(player)); playerMoveEvent.getPlayer().teleport(spawnPoints.get(playerMoveEvent.getPlayer()));
} }
if(playerMoveEvent.getNewPosition().y() < 40) { if(playerMoveEvent.getNewPosition().y() < 40) {
player.teleport(spawnPoints.get(player)); playerMoveEvent.getPlayer().teleport(spawnPoints.get(playerMoveEvent.getPlayer()));
this.scoreMap.putIfAbsent(player, 0);
this.scoreMap.put(player, this.scoreMap.get(player) + 1);
} }
} }

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ class Tetris extends StatelessGame {
public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) { public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) {
super(Dimension.THE_END.key, "Tetris", new PointsWinScore()); super(Dimension.THE_END.key, "Tetris", new PointsWinScore());
this.eventNode() eventNode()
.addListener(PlayerUseItemEvent.class, this::onPlayerInteract) .addListener(PlayerUseItemEvent.class, this::onPlayerInteract)
.addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack) .addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack)
.addListener(PlayerTickEvent.class, this::onPlayerTick); .addListener(PlayerTickEvent.class, this::onPlayerTick);
@@ -58,7 +58,7 @@ class Tetris extends StatelessGame {
protected void onStop() { protected void onStop() {
this.tetrisGames.forEach((player, tetrisGame) -> { this.tetrisGames.forEach((player, tetrisGame) -> {
tetrisGame.loose(); tetrisGame.loose();
this.getScore().insertResult(player, tetrisGame.getScore()); getScore().insertResult(player, tetrisGame.getScore());
tetrisGame.sidebar.removeViewer(player); tetrisGame.sidebar.removeViewer(player);
}); });
} }
@@ -106,25 +106,23 @@ class Tetris extends StatelessGame {
TetrisGame tetrisGame = this.tetrisGames.get(player); TetrisGame tetrisGame = this.tetrisGames.get(player);
if(tetrisGame == null) return; if(tetrisGame == null) return;
if(tetrisGame.lost && player.getGameMode() != GameMode.SPECTATOR) { if(tetrisGame.lost && player.getGameMode() != GameMode.SPECTATOR) {
this.letPlayerLoose(player); letPlayerLoose(player);
} }
} }
private void letPlayerLoose(Player player) { private void letPlayerLoose(Player player) {
TetrisGame tetrisGame = this.tetrisGames.get(player); TetrisGame tetrisGame = this.tetrisGames.get(player);
if(!this.getScore().hasResult(player)) {
player.setGameMode(GameMode.SPECTATOR); player.setGameMode(GameMode.SPECTATOR);
player.setInvisible(true); player.setInvisible(true);
this.getScore().insertResult(player, tetrisGame.getScore()); getScore().insertResult(player, tetrisGame.getScore());
}
boolean allGamesLost = this.tetrisGames.values().stream() boolean allGamesLost = this.tetrisGames.values().stream()
.filter(game -> !game.lost) .filter(game -> !game.lost)
.toList() .toList()
.isEmpty(); .isEmpty();
if(!this.setTimeLimit && !allGamesLost) { if(!setTimeLimit && !allGamesLost) {
this.setTimeLimit(90); this.setTimeLimit(90);
this.setTimeLimit = true; setTimeLimit = true;
} }
} }
@@ -136,7 +134,7 @@ class Tetris extends StatelessGame {
if(this.tetrisGames.get(p) == null) { if(this.tetrisGames.get(p) == null) {
this.tetrisGames.put(p, new TetrisGame( this.tetrisGames.put(p, new TetrisGame(
this, this,
this.getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*30, 0, 0), getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*30, 0, 0),
Tetromino.Shape.J, Tetromino.Shape.J,
this.nextTetrominoesCount, this.nextTetrominoesCount,
this.isFast, this.isFast,

View File

@@ -74,10 +74,10 @@ public class Playfield {
} }
} }
batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE);
batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE); batch.setBlock(getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE);
BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {}); BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {});
} }
@@ -90,7 +90,7 @@ public class Playfield {
if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false; if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false;
} }
if(isFullLine) { if(isFullLine) {
this.removeFullLine(y); removeFullLine(y);
removedLinesCounter += 1; removedLinesCounter += 1;
y -= 1; y -= 1;
} }
@@ -99,10 +99,10 @@ public class Playfield {
} }
public void addLines(int lines) { public void addLines(int lines) {
int xPosMissing = this.random.nextInt(1, 10); int xPosMissing = random.nextInt(1, 10);
for (int i = 0; i < lines; i++) { for (int i = 0; i < lines; i++) {
this.moveAllLinesUp(); moveAllLinesUp();
for (int x = 1; x < 11; x++) { for (int x = 1; x < 11; x++) {
if(x != xPosMissing) { if(x != xPosMissing) {
this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE); this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE);

View File

@@ -121,8 +121,8 @@ public class TetrisGame {
public void tick() { public void tick() {
if(this.lost || this.paused) return; if(this.lost || this.paused) return;
if(!this.currentTetromino.moveDown()) { if(!currentTetromino.moveDown()) {
this.setActiveTetrominoDown(); setActiveTetrominoDown();
} }
} }
@@ -176,7 +176,7 @@ public class TetrisGame {
} }
private boolean switchHold() { private boolean switchHold() {
if(!this.holdPossible) return false; if(!holdPossible) return false;
Tetromino newCurrentTetromino; Tetromino newCurrentTetromino;
if(this.holdTetromino == null) { if(this.holdTetromino == null) {
@@ -194,7 +194,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) this.loose(); if(!this.currentTetromino.moveDown()) loose();
double xChange = this.holdTetromino.getXChange(); double xChange = this.holdTetromino.getXChange();
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0)); this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
@@ -312,7 +312,7 @@ public class TetrisGame {
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
this.loose(); loose();
} }
} }
} }

View File

@@ -38,13 +38,13 @@ public class Tetromino {
this.uuid = UUID.randomUUID(); this.uuid = UUID.randomUUID();
switch (this.shape) { switch (this.shape) {
case I -> this.shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}}; case I -> shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}};
case J -> this.shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}}; case J -> shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}};
case L -> this.shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}}; case L -> shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}};
case O -> this.shapeArray = new int[][]{{1,1}, {1,1}}; case O -> shapeArray = new int[][]{{1,1}, {1,1}};
case S -> this.shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}}; case S -> shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}};
case T -> this.shapeArray = new int[][]{{0,1,0}, {1,1,1}, {0,0,0}}; case T -> shapeArray = new int[][]{{0,1,0}, {1,1,1}, {0,0,0}};
case Z -> this.shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}}; case Z -> shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}};
} }
} }
@@ -58,22 +58,22 @@ public class Tetromino {
public boolean rotate(boolean clockwise) { public boolean rotate(boolean clockwise) {
int[][] newShapeArray = this.getTurnedShapeArray(clockwise); int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
return this.checkCollisionAndMove(this.position, newShapeArray); return checkCollisionAndMove(this.position, newShapeArray);
} }
public boolean moveDown() { public boolean moveDown() {
Pos newPosition = this.position.sub(0, 1, 0); Pos newPosition = this.position.sub(0, 1, 0);
return this.checkCollisionAndMove(newPosition, this.shapeArray); return checkCollisionAndMove(newPosition, this.shapeArray);
} }
public boolean moveLeft() { public boolean moveLeft() {
Pos newPosition = this.position.sub(1, 0, 0); Pos newPosition = this.position.sub(1, 0, 0);
return this.checkCollisionAndMove(newPosition, this.shapeArray); return checkCollisionAndMove(newPosition, this.shapeArray);
} }
public boolean moveRight() { public boolean moveRight() {
Pos newPosition = this.position.add(1, 0, 0); Pos newPosition = this.position.add(1, 0, 0);
return this.checkCollisionAndMove(newPosition, this.shapeArray); return checkCollisionAndMove(newPosition, this.shapeArray);
} }
public void draw() { public void draw() {
@@ -83,11 +83,11 @@ public class Tetromino {
public void draw(boolean withGhost) { public void draw(boolean withGhost) {
if(withGhost) { if(withGhost) {
Pos ghostPos = this.position; Pos ghostPos = this.position;
while (!this.checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) { while (!checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
ghostPos = ghostPos.sub(0, 1, 0); ghostPos = ghostPos.sub(0, 1, 0);
} }
Pos positionChange = this.position.sub(ghostPos); Pos positionChange = this.position.sub(ghostPos);
this.getBlockPositions().forEach(pos -> { getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType); Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock()); ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock());
ghostBlock.setNoGravity(true); ghostBlock.setNoGravity(true);
@@ -97,11 +97,11 @@ public class Tetromino {
}); });
} }
this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock())); getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock()));
} }
public void drawAsEntities() { public void drawAsEntities() {
this.getBlockPositions().forEach(pos -> { getBlockPositions().forEach(pos -> {
Entity ghostBlock = new Entity(ghostEntityType); Entity ghostBlock = new Entity(ghostEntityType);
((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock()); ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock());
ghostBlock.setNoGravity(true); ghostBlock.setNoGravity(true);
@@ -222,10 +222,10 @@ public class Tetromino {
} }
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) { private boolean checkCollision(Pos newPosition, int[][] newShapeArray) {
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray); List<Pos> newBlockPositions = getBlockPositions(newPosition, newShapeArray);
for(Pos pos : newBlockPositions) { for(Pos pos : newBlockPositions) {
if(this.isPartOfTetromino(pos)) continue; if(isPartOfTetromino(pos)) continue;
if(this.instance.getBlock(pos) == this.getGhostBlock()) continue; if(this.instance.getBlock(pos) == this.getGhostBlock()) continue;
if(this.instance.getBlock(pos) != Block.AIR) return true; if(this.instance.getBlock(pos) != Block.AIR) return true;
} }
@@ -234,7 +234,7 @@ public class Tetromino {
} }
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) { private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!this.checkCollision(newPosition, newShapeArray)) { if(!checkCollision(newPosition, newShapeArray)) {
this.remove(); this.remove();
this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new); this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
this.setPosition(newPosition); this.setPosition(newPosition);

View File

@@ -0,0 +1,89 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.Tower;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.network.packet.server.SendablePacket;
import net.minestom.server.network.packet.server.play.ParticlePacket;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
public class Cursor extends Entity {
private boolean cursorActive = false;
private final int reach;
public Cursor(int reach) {
super(EntityType.ARMOR_STAND);
this.setBoundingBox(0,0,0);
this.reach = reach;
}
public void updateCursorPosition(Player player) {
Point targetPosition = player.getTargetBlockPosition(this.reach);
this.moveCursorTo(targetPosition);
}
private void moveCursorTo(@Nullable Point newTargetBlockPosition) {
this.setCursorEnabled(true);
if(newTargetBlockPosition == null) {
this.setCursorEnabled(false);
return;
}
if(!this.getInstance().getBlock(newTargetBlockPosition).equals(Block.BLACK_WOOL)) this.setCursorEnabled(false);
Material holdingMaterial = this.getInstance().getPlayerHandMaterial();
if(!this.getInstance().getGame().getAvailableTowers().containsKey(holdingMaterial)) this.setCursorEnabled(false);
this.teleport(new Pos(newTargetBlockPosition.add(0.5,1,0.5)));
this.showRange(this.getInstance().getGame().getAvailableTowers().get(holdingMaterial));
}
private void setCursorEnabled(boolean enabled) {
this.cursorActive = enabled;
this.setInvisible(!enabled);
}
private void showRange(@Nullable Class<? extends Tower> towerClass) {
if(towerClass == null) return;
if(!this.isCursorActive()) return;
try {
int range = towerClass.getConstructor().newInstance().getRange();
Collection<SendablePacket> particles = new ArrayList<>();
double circumference = 2 * Math.PI * range;
int count = (int) (circumference * 1.5);
for (int i = 0; i < count; i++) {
double radians = ((2 * Math.PI) / count) * i;
Vec relativePosition = new Vec(Math.sin(radians)*range, 0, Math.cos(radians)*range);
ParticlePacket particle = new ParticlePacket(Particle.COMPOSTER, this.position.add(relativePosition), Pos.ZERO, 0, 1);
particles.add(particle);
}
this.getInstance().getPlayer().sendPackets(particles);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public TowerdefenseRoom getInstance() {
return (TowerdefenseRoom) super.getInstance();
}
public boolean isCursorActive() {
return this.cursorActive;
}
public Block getTargetBlock() {
return this.getInstance().getBlock(this.getTargetBlockPosition());
}
public Pos getTargetBlockPosition() {
return this.getPosition().sub(0, 0.5, 0);
}
}

View File

@@ -2,39 +2,93 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies.WaveGenerator;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator; import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.*;
import eu.mhsl.minenet.minigames.score.LastWinsScore; import eu.mhsl.minenet.minigames.score.LastWinsScore;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.timer.Task;
import net.minestom.server.timer.TaskSchedule;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
public class Towerdefense extends StatelessGame { public class Towerdefense extends StatelessGame {
private final Random random = new Random(); private final Random random = new Random();
private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch(); private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
private final List<Pos> mazePath = new ArrayList<>(); private final List<Pos> mazePath = new ArrayList<>();
private List<TowerdefenseRoom> instances = new ArrayList<>(); private final List<TowerdefenseRoom> instances = new ArrayList<>();
private final WaveGenerator waveGenerator;
private Task waveTask;
private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
Material.BLAZE_SPAWN_EGG, BlazeTower.class,
Material.ENDERMAN_SPAWN_EGG, EndermanTower.class,
Material.PIGLIN_BRUTE_SPAWN_EGG, BruteTower.class,
Material.WITHER_SPAWN_EGG, WitherTower.class,
Material.ENDER_DRAGON_SPAWN_EGG, EnderDragonTower.class
);
private final Map<Class<? extends Tower>, Integer> prices = Map.of(
ZombieTower.class, 4,
SkeletonTower.class, 3,
BlazeTower.class, 8,
EndermanTower.class, 20,
BruteTower.class, 27,
WitherTower.class, 40,
EnderDragonTower.class, 200
);
private static final int pathLength = 10;
public Towerdefense() { public Towerdefense() {
super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore()); super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore());
setGenerator(new MazeGenerator()); this.setGenerator(new MazeGenerator());
this.generateMaze(); this.generateMaze();
this.waveGenerator = new WaveGenerator(this);
}
@Override
protected void checkAbandoned() {
this.scheduleNextTick((instance) -> {
if(this.instances.stream().allMatch(room -> room.getPlayers().isEmpty()) && this.getPlayers().isEmpty()) {
this.waveTask.cancel();
this.unload();
}
});
}
@Override
protected void onStart() {
this.getPlayers().forEach(player -> {
TowerdefenseRoom newRoom = new TowerdefenseRoom(player, this);
this.instances.add(newRoom);
player.setInstance(newRoom, new Pos(0, 1, 0));
});
this.waveTask = MinecraftServer.getSchedulerManager().scheduleTask(
this.waveGenerator::startNextWave,
TaskSchedule.seconds(3),
TaskSchedule.seconds(25)
);
} }
private void generateMaze() { private void generateMaze() {
Pos position = new Pos(0, 0, 0); Pos position = new Pos(0, 0, 0);
this.addMazePosition(position, Block.GREEN_WOOL); this.addMazePosition(position, Block.GREEN_WOOL);
position = position.add(0,0,2);
List<Integer> previousDirections = new ArrayList<>(); List<Integer> previousDirections = new ArrayList<>();
int direction = 1; // 0 -> right; 1 -> straight; 2 -> left int direction = 1; // 0 -> right; 1 -> straight; 2 -> left
for (int i = 0; i < 9; i++) { for (int i = 0; i < pathLength; i++) {
for (int j = 0; j < 3; j++) { for (int j = 0; j < 9; j++) {
position = position.add(direction-1,0,direction%2); position = position.add(direction-1,0,direction%2);
this.addMazePosition(position, Block.WHITE_WOOL); this.addMazePosition(position, Block.WHITE_WOOL);
} }
@@ -48,12 +102,16 @@ public class Towerdefense extends StatelessGame {
previousDirections.add(direction); previousDirections.add(direction);
} }
this.addMazePosition(position, Block.WHITE_WOOL); this.addMazePosition(position, Block.WHITE_WOOL);
this.addMazePosition(position.add(0,0,1), Block.WHITE_WOOL); this.addMazePosition(position.add(0,0,3), Block.WHITE_WOOL);
this.addMazePosition(position.add(0,0,2), Block.RED_WOOL); this.addMazePosition(position.add(0,0,6), Block.RED_WOOL);
} }
private void addMazePosition(Pos position, Block pathBlock) { private void addMazePosition(Pos position, Block pathBlock) {
this.mazeBatch.setBlock(position, pathBlock); for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
this.mazeBatch.setBlock(position.add(i-1,0,j-1), pathBlock);
}
}
this.mazePath.add(position.add(0.5,1,0.5)); this.mazePath.add(position.add(0.5,1,0.5));
} }
@@ -65,12 +123,21 @@ public class Towerdefense extends StatelessGame {
return this.mazePath; return this.mazePath;
} }
public List<TowerdefenseRoom> getInstances() {
return this.instances;
}
@Override @Override
protected boolean onPlayerJoin(Player p) { protected boolean onPlayerJoin(Player p) {
TowerdefenseRoom newRoom = new TowerdefenseRoom(p, this); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> p.teleport(new Pos((long) -this.getPlayers().size(), 1, 0)));
this.instances.add(newRoom); return super.onPlayerJoin(p);
p.setInstance(newRoom); }
newRoom.startWave(List.of(EntityType.ENDERMAN, EntityType.BLAZE, EntityType.PLAYER, EntityType.HORSE, EntityType.ARMOR_STAND, EntityType.SKELETON));
return false; public Map<Class<? extends Tower>, Integer> getPrices() {
return this.prices;
}
public Map<Material, Class<? extends Tower>> getAvailableTowers() {
return this.availableTowers;
} }
} }

View File

@@ -1,22 +1,47 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense; package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies.GroupFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator; import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.Tower;
import eu.mhsl.minenet.minigames.util.BatchUtil; import eu.mhsl.minenet.minigames.util.BatchUtil;
import eu.mhsl.minenet.minigames.util.CommonEventHandles;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.collision.Aerodynamics;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.*; import net.minestom.server.entity.*;
import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.entity.EntityDeathEvent;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithBlockEvent;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithEntityEvent;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.event.item.ItemDropEvent;
import net.minestom.server.event.item.PlayerBeginItemUseEvent;
import net.minestom.server.event.player.*;
import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemAnimation;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.timer.TaskSchedule; import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public class TowerdefenseRoom extends InstanceContainer { public class TowerdefenseRoom extends InstanceContainer {
private final static int reach = 30;
private long lastPlayerAttack = 0;
private final Player player; private final Player player;
private final Towerdefense game; private final Towerdefense game;
private final List<EntityCreature> enemies = new ArrayList<>();
private final List<Tower> towers = new ArrayList<>();
private final Cursor cursor;
private int money = 0;
public TowerdefenseRoom(Player player, Towerdefense game) { public TowerdefenseRoom(Player player, Towerdefense game) {
super(UUID.randomUUID(), Dimension.OVERWORLD.key); super(UUID.randomUUID(), Dimension.OVERWORLD.key);
@@ -25,34 +50,189 @@ public class TowerdefenseRoom extends InstanceContainer {
this.game = game; this.game = game;
this.player.setGameMode(GameMode.ADVENTURE); this.player.setGameMode(GameMode.ADVENTURE);
this.player.setAllowFlying(true); this.player.setAllowFlying(true);
this.player.getInventory().setItemStack(0, ItemStack.of(Material.ARMOR_STAND)); this.addMoney(0);
this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).setBaseValue(reach);
this.player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(reach);
setGenerator(new MazeGenerator()); this.player.getInventory().addItemStack(
ItemStack.of(Material.BOW).withCustomName(Component.text("Schießen", TextColor.color(255, 180, 0)))
);
this.player.getInventory().addItemStack(
ItemStack.of(Material.BARRIER).withCustomName(Component.text("Löschen", TextColor.color(255,0,0)))
);
this.game.getAvailableTowers().forEach((material, tower) -> {
int price = this.game.getPrices().get(tower);
this.player.getInventory().addItemStack(ItemStack.of(material).withMaxStackSize(price).withAmount(price));
});
this.setGenerator(new MazeGenerator());
BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {}); BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {});
this.cursor = new Cursor(reach);
this.cursor.setInstance(this);
this.cursor.setBoundingBox(0,0,0);
this.eventNode()
.addListener(EntityDeathEvent.class, event -> {
if(!(event.getEntity() instanceof EntityCreature enemy)) return;
this.enemies.remove(enemy);
})
.addListener(PlayerTickEvent.class, event -> this.cursor.updateCursorPosition(this.player))
.addListener(PlayerUseItemEvent.class, event -> this.useItem())
.addListener(PlayerUseItemOnBlockEvent.class, event -> this.useItem())
.addListener(PlayerEntityInteractEvent.class, event -> this.useItem())
.addListener(PlayerHandAnimationEvent.class, event -> this.useItem())
.addListener(ItemDropEvent.class, CommonEventHandles::cancel)
.addListener(InventoryPreClickEvent.class, CommonEventHandles::cancel)
.addListener(PlayerSwapItemEvent.class, CommonEventHandles::cancel)
.addListener(PlayerBeginItemUseEvent.class, event -> {
if(event.getAnimation().equals(ItemAnimation.BOW)) event.setCancelled(true);
});
} }
public void startWave(List<EntityType> entities) { private void useItem() {
int counter = 0; Material itemInHand = this.player.getItemInMainHand().material();
for(EntityType entityType : entities) { if(itemInHand.equals(Material.BOW)) {
MinecraftServer.getSchedulerManager().scheduleTask(() -> { this.playerAttack();
this.addEntity(new EntityCreature(entityType));
return TaskSchedule.stop();
}, TaskSchedule.millis(800L*counter));
counter++;
}
}
private void addEntity(EntityCreature entity) {
entity.setInstance(this, this.game.getMazePath().getFirst());
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15);
entity.getNavigator().setPathTo(this.game.getMazePath().get(1), 0.7, () -> changeEntityGoal(entity, 1));
}
private void changeEntityGoal(EntityCreature entity, int positionIndex) {
if(positionIndex == this.game.getMazePath().size()-1) {
return; return;
} }
entity.getNavigator().setPathTo(this.game.getMazePath().get(positionIndex+1), 0.7, () -> changeEntityGoal(entity, positionIndex+1)); if(itemInHand.equals(Material.BARRIER)) {
this.removeTower();
return;
}
this.placeTower();
} }
private void playerAttack() {
if(this.lastPlayerAttack > System.currentTimeMillis()-200) return;
Player p = this.player;
this.lastPlayerAttack = System.currentTimeMillis();
EntityProjectile projectile = new EntityProjectile(p, EntityType.SPECTRAL_ARROW);
projectile.setView(p.getPosition().yaw(), p.getPosition().pitch());
projectile.setNoGravity(true);
projectile.setAerodynamics(new Aerodynamics(0, 1, 1));
Vec projectileVelocity = p.getPosition().direction().normalize().mul(20);
projectile.setVelocity(projectileVelocity);
projectile.scheduleRemove(Duration.ofSeconds(5));
projectile.setInstance(this, p.getPosition().add(0, p.getEyeHeight()-0.5, 0));
projectile.eventNode()
.addListener(ProjectileCollideWithEntityEvent.class, hitEvent -> {
if(!(hitEvent.getTarget() instanceof EntityCreature target)) return;
if(!this.getEnemies().stream().filter(entityCreature -> !entityCreature.isDead()).toList().contains(target)) return;
target.damage(DamageType.PLAYER_ATTACK, 0.1f);
if(target.isDead()) this.addMoney((int) target.getAttribute(Attribute.MAX_HEALTH).getBaseValue());
projectile.remove();
})
.addListener(ProjectileCollideWithBlockEvent.class, collisionEvent -> projectile.remove());
}
public void addMoney(int amount) {
this.money += amount;
this.player.setLevel(this.money);
}
public Material getPlayerHandMaterial() {
return this.player.getItemInMainHand().material();
}
private synchronized void placeTower() {
if(!this.canPlaceActiveTower()) {
if(this.cursor.isCursorActive() && !this.enoughMoneyForActiveTower()) {
this.player.sendActionBar(Component.text("Nicht genug Geld!", TextColor.color(255,0,0)));
}
return;
}
try {
Material usedMaterial = this.player.getItemInMainHand().material();
Tower tower = this.game.getAvailableTowers().entrySet().stream()
.filter(materialClassEntry -> materialClassEntry.getKey().equals(usedMaterial))
.findFirst()
.orElseThrow()
.getValue()
.getConstructor()
.newInstance();
this.setBlock(this.cursor.getTargetBlockPosition(), Block.BLUE_WOOL);
tower.setInstance(this, this.cursor.getPosition());
this.towers.add(tower);
tower.startShooting();
this.addMoney(-this.game.getPrices().get(tower.getClass()));
} catch (Exception ignored) {
}
this.cursor.updateCursorPosition(this.player);
}
private void removeTower() {
Entity entity = this.player.getLineOfSightEntity(reach, entity1 -> true);
if(!(entity instanceof Tower tower)) return;
if(!this.towers.contains(tower)) return;
this.setBlock(tower.getPosition().sub(0, 0.5, 0), Block.BLACK_WOOL);
this.addMoney(tower.getSellingPrice(this.game.getPrices().get(tower.getClass())));
this.towers.remove(tower);
tower.remove();
}
private boolean canPlaceActiveTower() {
Material usedMaterial = this.player.getItemInMainHand().material();
return this.canPlaceTower(this.game.getAvailableTowers().get(usedMaterial));
}
private boolean canPlaceTower(@Nullable Class<? extends Tower> towerClass) {
if(towerClass == null) return false;
return this.cursor.isCursorActive() && this.enoughMoney(towerClass);
}
private boolean enoughMoneyForActiveTower() {
Material usedMaterial = this.player.getItemInMainHand().material();
return this.enoughMoney(this.game.getAvailableTowers().get(usedMaterial));
}
private boolean enoughMoney(@Nullable Class<? extends Tower> towerClass) {
if(towerClass == null) return true;
return this.money >= this.game.getPrices().get(towerClass);
}
void startWave(List<GroupFactory> enemyGroups) {
enemyGroups.forEach(groupFactory -> groupFactory.summonGroup(this));
}
public void addEnemy(EntityCreature enemy) {
enemy.setInstance(this, this.game.getMazePath().getFirst());
this.enemies.add(enemy);
this.changeEnemyGoal(enemy, 0);
}
private synchronized void changeEnemyGoal(EntityCreature enemy, int positionIndex) {
if(positionIndex == this.game.getMazePath().size()-1) {
this.enemies.remove(enemy);
enemy.remove();
float damage = (float) Math.ceil(enemy.getHealth()/10);
if(this.player.getHealth() - damage <= 0) {
this.getEnemies().forEach(Entity::remove);
this.towers.forEach(Entity::remove);
this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).setBaseValue(this.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).attribute().defaultValue());
this.player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(this.player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).attribute().defaultValue());
this.player.setInstance(this.game).thenRun(() -> MinecraftServer.getInstanceManager().unregisterInstance(this));
this.player.heal();
this.game.getScore().insertResult(this.player);
return;
}
this.player.damage(DamageType.PLAYER_ATTACK, damage);
return;
}
enemy.getNavigator().setPathTo(this.game.getMazePath().get(positionIndex+1), 0.6, () -> this.changeEnemyGoal(enemy, positionIndex+1));
}
public Towerdefense getGame() {
return this.game;
}
public List<EntityCreature> getEnemies() {
return this.enemies;
}
public Player getPlayer() {
return this.player;
}
} }

View File

@@ -0,0 +1,33 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.attribute.Attribute;
public record EnemyFactory(EntityType entityType, float health, double speed) {
/**
* Factory for a tower defense enemy.
* @param entityType type of enemy
* @param health base health (between 0 and 1024, default 10)
* @param speed walk speed (default 0.1)
*/
public EnemyFactory {
if(health > 1024 || health <= 0) throw new IllegalArgumentException("Enemy health has to be between 0 and 1024");
}
public EnemyFactory(EntityType entityType) {
this(entityType, 10, 0.1);
}
public EntityCreature buildEntity() {
EntityCreature entity = new EntityCreature(this.entityType);
entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(this.speed);
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(this.health);
entity.setHealth(this.health);
return entity;
}
public int getStrength() {
return (int) Math.floor(this.health * this.speed * 5);
}
}

View File

@@ -0,0 +1,16 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseRoom;
import net.minestom.server.MinecraftServer;
import net.minestom.server.timer.TaskSchedule;
public record GroupFactory(EnemyFactory enemyFactory, int count, long delay) {
public void summonGroup(TowerdefenseRoom instance) {
for (int i = 0; i < this.count; i++) {
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
instance.addEnemy(this.enemyFactory.buildEntity());
return TaskSchedule.stop();
}, TaskSchedule.millis(this.delay*i));
}
}
}

View File

@@ -0,0 +1,77 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.Towerdefense;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.EntityType;
import net.minestom.server.timer.TaskSchedule;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
public class WaveGenerator {
final Towerdefense game;
int currentLevel;
Random random = new Random();
private final Map<EnemyFactory, Integer> availableEnemies = Map.of(
new EnemyFactory(EntityType.PIG, 2, 0.1), new EnemyFactory(EntityType.PIG, 2, 0.1).getStrength(),
new EnemyFactory(EntityType.VILLAGER, 6, 0.1), new EnemyFactory(EntityType.VILLAGER, 6, 0.1).getStrength(),
new EnemyFactory(EntityType.SHEEP, 4, 0.2), new EnemyFactory(EntityType.SHEEP, 4, 0.2).getStrength(),
new EnemyFactory(EntityType.COW, 10, 0.1), new EnemyFactory(EntityType.COW, 10, 0.1).getStrength(),
new EnemyFactory(EntityType.CAMEL, 10, 0.2), new EnemyFactory(EntityType.CAMEL, 10, 0.2).getStrength(),
new EnemyFactory(EntityType.ARMADILLO, 20, 0.4), new EnemyFactory(EntityType.ARMADILLO, 20, 0.3).getStrength(),
new EnemyFactory(EntityType.CHICKEN, 8, 0.5), new EnemyFactory(EntityType.CHICKEN, 8, 0.6).getStrength()
);
public WaveGenerator(Towerdefense game) {
this(game, 1);
}
public WaveGenerator(Towerdefense game, int startLevel) {
this.game = game;
this.currentLevel = startLevel - 1;
}
public void startNextWave() {
this.currentLevel += 1;
List<EnemyFactory> enemyList = this.chooseEnemies();
Collections.shuffle(enemyList);
int averageDelay = Math.min(20000 / enemyList.size(), 1500);
AtomicInteger delay = new AtomicInteger(0);
enemyList.forEach(enemy -> {
delay.addAndGet(averageDelay);
MinecraftServer.getSchedulerManager().scheduleTask(
() -> this.spawnEnemy(enemyList.removeFirst()),
TaskSchedule.millis(delay.get() + this.random.nextInt(averageDelay-200, averageDelay+200)),
TaskSchedule.stop()
);
});
}
private void spawnEnemy(EnemyFactory enemy) {
this.game.getInstances().forEach(instance -> instance.addEnemy(enemy.buildEntity()));
}
private int getCurrentLevelStrength() {
return (int) (0.5 * Math.pow(2, this.currentLevel));
}
private List<EnemyFactory> chooseEnemies() {
int strength = this.getCurrentLevelStrength();
final List<EnemyFactory> usedEnemies = new ArrayList<>();
while(strength > 0) {
int finalStrength = strength;
Map.Entry<EnemyFactory, Integer> chosenEnemy = this.availableEnemies.entrySet().stream()
.filter(e -> e.getValue() <= finalStrength)
.sorted((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2))
.findAny()
.get();
usedEnemies.add(chosenEnemy.getKey());
strength -= chosenEnemy.getKey().getStrength();
}
return usedEnemies;
}
}

View File

@@ -0,0 +1,16 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
public class BlazeTower extends ShootingTower {
public BlazeTower() {
super(EntityType.BLAZE, 2, 2, 15);
}
@Override
protected void attack(EntityCreature enemy) {
this.swingMainHand();
this.shootProjectile(enemy, EntityType.FIREBALL, 2, 0);
}
}

View File

@@ -0,0 +1,19 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
public class BruteTower extends Tower {
public BruteTower() {
super(EntityType.PIGLIN_BRUTE, 20, 2, 4);
this.setItemInMainHand(ItemStack.of(Material.GOLDEN_AXE));
}
@Override
protected void attack(EntityCreature enemy) {
this.swingMainHand();
this.causeDamage(enemy);
}
}

View File

@@ -0,0 +1,20 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
public class EnderDragonTower extends ShootingTower{
public EnderDragonTower() {
super(EntityType.ENDER_DRAGON, 40, 7, 20);
}
@Override
protected void attack(EntityCreature enemy) {
this.shootProjectile(enemy, EntityType.DRAGON_FIREBALL, 2, 0);
}
@Override
protected void onProjectileHit(EntityCreature enemy) {
super.onProjectileHit(enemy);
}
}

View File

@@ -0,0 +1,16 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
public class EndermanTower extends Tower {
public EndermanTower() {
super(EntityType.ENDERMAN, 12, 1, 6);
}
@Override
protected void attack(EntityCreature enemy) {
this.swingMainHand();
this.causeDamage(enemy);
}
}

View File

@@ -0,0 +1,63 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.collision.Aerodynamics;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityProjectile;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithBlockEvent;
import net.minestom.server.event.entity.projectile.ProjectileCollideWithEntityEvent;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
public abstract class ShootingTower extends Tower {
public ShootingTower(@NotNull EntityType entityType, int damage, double attacksPerSecond, int range) {
super(entityType, damage, attacksPerSecond, range);
}
protected void shootProjectile(EntityCreature enemy, EntityType projectileType, double power, double spread) {
EntityProjectile projectile = new EntityProjectile(this, projectileType);
projectile.setView(this.getPosition().yaw(), this.getPosition().pitch());
projectile.setNoGravity(true);
projectile.setAerodynamics(new Aerodynamics(0, 1, 1));
Pos startingPoint = this.getPosition().add(0, this.getEyeHeight(), 0);
double enemySpeed = enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue() / 0.05;
Point enemyGoal = enemy.getNavigator().getGoalPosition();
if(enemyGoal == null) enemyGoal = enemy.getPosition();
Pos enemyPosition = enemy.getPosition();
Vec enemyMovement = Vec.fromPoint(enemyGoal.sub(enemyPosition)).normalize().mul(enemySpeed);
// just an approximation, not calculated correctly:
double projectileSpeed = 20 * power;
double enemyTowerDistance = startingPoint.distance(enemyPosition);
double estimatedFlightTime = (enemyTowerDistance / projectileSpeed);
Pos targetPosition = enemyPosition.add(enemyMovement.mul(estimatedFlightTime)).withY(enemyPosition.y()+enemy.getEyeHeight());
projectile.shoot(targetPosition, power, spread);
projectile.scheduleRemove(Duration.ofSeconds(5));
projectile.setInstance(this.getInstance(), startingPoint);
projectile.eventNode()
.addListener(ProjectileCollideWithEntityEvent.class, event -> {
if(!(event.getTarget() instanceof EntityCreature target)) return;
if(!this.getRoomInstance().getEnemies().contains(target)) return;
this.causeDamage(target);
this.onProjectileHit(target);
target.setFlyingWithElytra(true);
projectile.remove();
})
.addListener(ProjectileCollideWithBlockEvent.class, event -> projectile.remove());
}
protected void onProjectileHit(EntityCreature enemy) {
}
@Override
protected void attack(EntityCreature enemy) {
this.shootProjectile(enemy, EntityType.ARROW, 2, 0);
}
}

View File

@@ -0,0 +1,25 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
public class SkeletonTower extends ShootingTower {
public SkeletonTower() {
super(EntityType.SKELETON, 1, 2, 10);
this.setItemInMainHand(ItemStack.of(Material.BOW));
}
@Override
protected void attack(EntityCreature enemy) {
this.swingMainHand();
this.shootProjectile(enemy, EntityType.ARROW, 2, 0);
}
@Override
protected void onProjectileHit(EntityCreature enemy) {
super.onProjectileHit(enemy);
// enemy.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(enemy.getAttribute(Attribute.MOVEMENT_SPEED).getBaseValue()*0.5);
}
}

View File

@@ -0,0 +1,135 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseRoom;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.*;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.timer.Task;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.stream.Stream;
public abstract class Tower extends EntityCreature {
public enum Priority {
FIRST(Type.PATH_DISTANCE),
LAST(Type.PATH_DISTANCE),
STRONG(Type.HEALTH),
WEAK(Type.HEALTH),
CLOSE(Type.TOWER_DISTANCE),
FAR(Type.TOWER_DISTANCE);
final Type type;
Priority(Type type) {
this.type = type;
}
public Type getType() {
return this.type;
}
public enum Type {
PATH_DISTANCE,
HEALTH,
TOWER_DISTANCE
}
}
private final static int damageDivider = 10;
private Priority priority = Priority.FIRST;
protected float damage;
protected int range;
protected double attacksPerSecond;
protected TaskSchedule attackDelay;
private @Nullable Task attackTask;
private float sellingPriceMultiplier = 1;
public Tower(@NotNull EntityType entityType, int damage, double attacksPerSecond, int range) {
super(entityType);
this.damage = (float) damage / damageDivider;
this.range = range;
this.attacksPerSecond = attacksPerSecond;
this.attackDelay = TaskSchedule.millis((long) (1000/attacksPerSecond));
}
public void startShooting() {
this.attackTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
EntityCreature nextEnemy = this.getNextEnemy();
if(nextEnemy == null) return this.attackDelay;
this.lookAt(nextEnemy);
this.attack(nextEnemy);
return this.attackDelay;
}, TaskSchedule.immediate());
}
@Override
protected void remove(boolean permanent) {
if(this.attackTask != null) this.attackTask.cancel();
super.remove(permanent);
}
private @Nullable EntityCreature getNextEnemy() {
List<EntityCreature> enemies = this.getSortedEnemies();
if(enemies.isEmpty()) return null;
return switch (this.priority) {
case LAST, STRONG, FAR -> enemies.getLast();
case FIRST, WEAK, CLOSE -> enemies.getFirst();
};
}
private List<EntityCreature> getSortedEnemies() {
Stream<EntityCreature> enemyStream = this.getRoomInstance().getEnemies().stream().parallel()
.filter(enemy -> !enemy.isDead())
.filter(enemy -> enemy.getPosition().distance(this.getPosition()) <= this.range);
return switch (this.priority.getType()) {
case PATH_DISTANCE -> enemyStream
.sorted((o1, o2) -> {
Pos endPoint = this.getRoomInstance().getGame().getMazePath().getLast();
return Double.compare(
o1.getPosition().distance(endPoint),
o2.getPosition().distance(endPoint)
);
}).toList();
case TOWER_DISTANCE -> enemyStream
.sorted((o1, o2) -> Double.compare(
o1.getPosition().distance(this.getPosition()),
o2.getPosition().distance(this.getPosition())
)).toList();
case HEALTH -> enemyStream
.sorted((o1, o2) -> Float.compare(o1.getHealth(), o2.getHealth()))
.toList();
};
}
protected TowerdefenseRoom getRoomInstance() {
return (TowerdefenseRoom) this.getInstance();
}
public int getSellingPrice(int buyPrice) {
return (int) (this.sellingPriceMultiplier * buyPrice);
}
public double getValue() {
return this.damage * this.attacksPerSecond * this.range * 2;
}
public int getRange() {
return this.range;
}
protected void causeDamage(EntityCreature enemy) {
this.causeDamage(enemy, this.damage);
}
protected void causeDamage(EntityCreature enemy, float amount) {
enemy.damage(DamageType.PLAYER_ATTACK, amount);
if(enemy.isDead()) this.getRoomInstance().addMoney((int) enemy.getAttribute(Attribute.MAX_HEALTH).getBaseValue());
}
protected abstract void attack(EntityCreature enemy);
}

View File

@@ -0,0 +1,20 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
public class WitherTower extends ShootingTower {
public WitherTower() {
super(EntityType.WITHER, 18, 4, 13);
}
@Override
protected void attack(EntityCreature enemy) {
this.shootProjectile(enemy, EntityType.WITHER_SKULL, 2, 0);
}
@Override
protected void onProjectileHit(EntityCreature enemy) {
super.onProjectileHit(enemy);
}
}

View File

@@ -0,0 +1,19 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
public class ZombieTower extends Tower {
public ZombieTower() {
super(EntityType.ZOMBIE, 7, 1, 4);
this.setItemInMainHand(ItemStack.of(Material.IRON_SWORD));
}
@Override
protected void attack(EntityCreature enemy) {
this.swingMainHand();
this.causeDamage(enemy);
}
}

View File

@@ -156,7 +156,6 @@ class TrafficLightRace extends StatelessGame {
@Override @Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
if(isBeforeBeginning) return; if(isBeforeBeginning) return;
if(getScore().hasResult(playerMoveEvent.getPlayer())) return;
if(phase.equals(LightPhase.RED) && playerMoveEvent.getNewPosition().z()-0.01 > playerMoveEvent.getPlayer().getPosition().z()) { if(phase.equals(LightPhase.RED) && playerMoveEvent.getNewPosition().z()-0.01 > playerMoveEvent.getPlayer().getPosition().z()) {
playerMoveEvent.getPlayer().setVelocity(new Vec(0, 8, -15)); playerMoveEvent.getPlayer().setVelocity(new Vec(0, 8, -15));

View File

@@ -1,254 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,110 +0,0 @@
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,11 +55,7 @@ public class JoinInventory extends InteractableInventory {
if(target.isPresent()) if(target.isPresent())
Room.setRoom(player, target.get()); Room.setRoom(player, target.get());
else else
new ChatMessage(Icon.ERROR) new ChatMessage(Icon.ERROR).appendTranslated("hub#join_notFound").appendStatic(" " + typedText).send(player);
.appendTranslated("hub#join_notFound")
.appendSpace()
.quote(typedText.trim())
.send(player);
} }
private String formatInput(String raw) { private String formatInput(String raw) {

View File

@@ -15,6 +15,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode; import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.event.player.PlayerBlockBreakEvent; import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.instance.anvil.AnvilLoader; import net.minestom.server.instance.anvil.AnvilLoader;
@@ -66,6 +67,9 @@ public class Room extends MineNetInstance implements Spawnable {
p.getInventory().clear(); p.getInventory().clear();
p.setGameMode(GameMode.ADVENTURE); p.setGameMode(GameMode.ADVENTURE);
p.setInvisible(false); p.setInvisible(false);
p.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).setBaseValue(p.getAttribute(Attribute.BLOCK_INTERACTION_RANGE).attribute().defaultValue());
p.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(p.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).attribute().defaultValue());
p.heal();
p.setExp(0); p.setExp(0);
p.setLevel(0); p.setLevel(0);
rooms.add(room); rooms.add(room);
@@ -90,29 +94,29 @@ public class Room extends MineNetInstance implements Spawnable {
private Room(Player owner) { private Room(Player owner) {
super(Dimension.THE_END.key); super(Dimension.THE_END.key);
this.apiDriven = false; this.apiDriven = false;
this.construct(); construct();
this.setOwner(owner); setOwner(owner);
} }
protected Room() { protected Room() {
super(Dimension.THE_END.key); super(Dimension.THE_END.key);
this.apiDriven = true; this.apiDriven = true;
this.construct(); construct();
} }
private void construct() { private void construct() {
MinecraftServer.getInstanceManager().registerInstance(this); MinecraftServer.getInstanceManager().registerInstance(this);
this.setChunkLoader(new AnvilLoader(Resource.LOBBY_MAP.getPath())); setChunkLoader(new AnvilLoader(Resource.LOBBY_MAP.getPath()));
this.gameSelector = new GameSelector(); this.gameSelector = new GameSelector();
this.gameSelector.setInstance(this, new Pos(0.5, 50, 19.5)); this.gameSelector.setInstance(this, new Pos(0.5, 50, 19.5));
this.eventNode().addListener(PlayerBlockBreakEvent.class, CommonEventHandles::cancel); eventNode().addListener(PlayerBlockBreakEvent.class, CommonEventHandles::cancel);
this.eventNode().addListener(PlayerDisconnectEvent.class, playerDisconnectEvent -> unsetRoom(playerDisconnectEvent.getPlayer())); eventNode().addListener(PlayerDisconnectEvent.class, playerDisconnectEvent -> unsetRoom(playerDisconnectEvent.getPlayer()));
} }
public Player getOwner() { public Player getOwner() {
return this.owner; return owner;
} }
public void setOwner(Player newOwner) { public void setOwner(Player newOwner) {
@@ -125,7 +129,7 @@ public class Room extends MineNetInstance implements Spawnable {
if(p != this.owner) return; if(p != this.owner) return;
this.getAllMembers().stream() getAllMembers().stream()
.filter(player -> player != p) // exclude the current leaving owner .filter(player -> player != p) // exclude the current leaving owner
.findFirst() .findFirst()
.ifPresentOrElse( .ifPresentOrElse(
@@ -135,17 +139,9 @@ public class Room extends MineNetInstance implements Spawnable {
Room.unsetRoom(p); Room.unsetRoom(p);
new ChatMessage(Icon.ERROR) new ChatMessage(Icon.ERROR).appendStatic("The room leader left!").send(getAllMembers());
.appendTranslated("room#ownerLeft") new ChatMessage(Icon.SCIENCE).appendStatic(this.owner.getUsername()).appendStatic(" is the new Leader!").send(getAllMembers().stream().filter(player -> player != this.owner).collect(Collectors.toSet()));
.send(this.getAllMembers()); new ChatMessage(Icon.SUCCESS).appendStatic("You are now the leader.").send(this.owner);
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,9 +37,7 @@ public class GameSelector extends InteractableEntity {
if(playerEntityInteractEvent.getPlayer() != room.getOwner()) { if(playerEntityInteractEvent.getPlayer() != room.getOwner()) {
abstractVillagerMeta.setHeadShakeTimer(20); abstractVillagerMeta.setHeadShakeTimer(20);
new ChatMessage(Icon.ERROR) new ChatMessage(Icon.ERROR).appendStatic("Only the room leader can start games!").send(playerEntityInteractEvent.getPlayer());
.appendTranslated("room#onlyOwnerCanStart")
.send(playerEntityInteractEvent.getPlayer());
return; return;
} }

View File

@@ -53,7 +53,7 @@ public class Languages {
for(String line : Files.readAllLines(locale.toPath())) { for(String line : Files.readAllLines(locale.toPath())) {
//line = line.replaceAll("[^\\p{L}\\s,#_+.:;]+", ""); //line = line.replaceAll("[^\\p{L}\\s,#_+.:;]+", "");
line = line.replaceAll("[^a-zA-Z0-9äöüÄÖÜ ,:;#!_+]", ""); line = line.replaceAll("[^a-zA-Z0-9äöüÄÖÜ ,:;#_+]", "");
String[] columns = line.split(";"); String[] columns = line.split(";");
if(columns.length < 1) continue; if(columns.length < 1) continue;

View File

@@ -8,10 +8,7 @@ public enum Icon {
STAR("\u2606", NamedTextColor.GOLD), STAR("\u2606", NamedTextColor.GOLD),
CHAT("\u276F\u276F", NamedTextColor.WHITE), CHAT("\u276F\u276F", NamedTextColor.WHITE),
SUCCESS("\u2714", NamedTextColor.GREEN), 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 String symbol;
private final NamedTextColor color; private final NamedTextColor color;

View File

@@ -31,23 +31,8 @@ public abstract class TranslatableMessage implements Sendable {
return this; return this;
} }
public TranslatableMessage appendSpace() {
chain.add(Component.text(" "));
return this;
}
public TranslatableMessage appendTranslated(String mapId) { public TranslatableMessage appendTranslated(String mapId) {
chain.add(TranslatedComponent.byId(mapId).setColor(NamedTextColor.WHITE)); chain.add(TranslatedComponent.byId(mapId));
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; return this;
} }

View File

@@ -1,29 +0,0 @@
package eu.mhsl.minenet.minigames.score;
import eu.mhsl.minenet.minigames.util.MapUtil;
import net.minestom.server.entity.Player;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class LowestPointsWinScore extends PointsWinScore {
@Override
protected void insertResultImplementation(Set<Player> p, int currentPoints) {
Set<Player> combined = scoreOrder.entrySet().stream()
.filter(entrySet -> Objects.equals(entrySet.getValue(), currentPoints))
.map(Map.Entry::getKey)
.findFirst()
.orElseGet(() -> {
Set<Player> s = new HashSet<>();
scoreOrder.put(s, currentPoints);
return s;
});
combined.addAll(p);
this.scoreOrder = MapUtil.sortReversedByValue(this.scoreOrder);
getScores().clear();
this.scoreOrder.forEach((player, integer) -> getScores().addFirst(player));
}
}

View File

@@ -12,21 +12,11 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class PointsWinScore extends Score { public class PointsWinScore extends Score {
Map<Set<Player>, Integer> scoreOrder = new HashMap<>(); private Map<Set<Player>, Integer> scoreOrder = new HashMap<>();
@Override @Override
protected void insertResultImplementation(Set<Player> p, int currentPoints) { protected void insertResultImplementation(Set<Player> p, int currentPoints) {
Set<Player> combined = scoreOrder.entrySet().stream() this.scoreOrder.put(p, currentPoints);
.filter(entrySet -> Objects.equals(entrySet.getValue(), currentPoints))
.map(Map.Entry::getKey)
.findFirst()
.orElseGet(() -> {
Set<Player> s = new HashSet<>();
scoreOrder.put(s, currentPoints);
return s;
});
combined.addAll(p);
this.scoreOrder = MapUtil.sortByValue(this.scoreOrder); this.scoreOrder = MapUtil.sortByValue(this.scoreOrder);
getScores().clear(); getScores().clear();
this.scoreOrder.forEach((player, integer) -> getScores().addFirst(player)); this.scoreOrder.forEach((player, integer) -> getScores().addFirst(player));
@@ -52,12 +42,13 @@ public class PointsWinScore extends Score {
.filter(player -> !player.getUsername().isBlank()) .filter(player -> !player.getUsername().isBlank())
.toList() .toList()
.isEmpty()) .isEmpty())
.map(players -> players.stream() .map(players -> players
.map(Player::getUsername) .stream()
.sorted() .filter(player -> scoreOrder.get(Set.of(player)) != null)
.map(player -> player.getUsername()+" : "+scoreOrder.get(Set.of(player)).toString())
.collect(Collectors.joining(", ")) .collect(Collectors.joining(", "))
+ " : " + scoreOrder.get(players) )
).toList() .toList()
) )
.undent() .undent()
.newLine() .newLine()

View File

@@ -4,7 +4,6 @@ import eu.mhsl.minenet.minigames.score.Score;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
public class Tournament { public class Tournament {
private final List<Score> gameScores = new ArrayList<>(); private final List<Score> gameScores = new ArrayList<>();
@@ -44,11 +43,9 @@ public class Tournament {
public Rewards getRewards() { public Rewards getRewards() {
Map<UUID, Integer> itemCount = new HashMap<>(); Map<UUID, Integer> itemCount = new HashMap<>();
int count = 0; int count = 0;
for (Set<Player> players : getPlaces()) { for (Player player : getPlaces()) {
if(count >= this.rewardConfiguration.rewardCount().size()) break; if(count >= this.rewardConfiguration.rewardCount().size()) break;
for(Player player : players) {
itemCount.put(player.getUuid(), this.rewardConfiguration.rewardCount().get(count)); itemCount.put(player.getUuid(), this.rewardConfiguration.rewardCount().get(count));
}
count++; count++;
} }
@@ -56,21 +53,18 @@ public class Tournament {
this.memorialConfiguration.memorialMaterial().namespace().value(), this.memorialConfiguration.memorialMaterial().namespace().value(),
this.memorialConfiguration.memorialTitle(), this.memorialConfiguration.memorialTitle(),
this.memorialConfiguration.memorialLore(), this.memorialConfiguration.memorialLore(),
getGameScores().keySet().stream().map(Player::getUuid).toList(), getPlaces().stream().map(Player::getUuid).toList(),
this.rewardConfiguration.item().namespace().value(), this.rewardConfiguration.item().namespace().value(),
itemCount itemCount
); );
} }
public List<Set<Player>> getPlaces() { public List<Player> getPlaces() {
List<Set<Player>> players = new ArrayList<>( List<Player> players = new ArrayList<>(
getGameScores().entrySet().stream() getGameScores().entrySet().stream()
.collect( .sorted(Map.Entry.comparingByValue())
Collectors.groupingBy( .map(Map.Entry::getKey)
Map.Entry::getValue, .toList()
Collectors.mapping(Map.Entry::getKey, Collectors.toSet())
)
).values()
); );
Collections.reverse(players); Collections.reverse(players);

View File

@@ -12,12 +12,10 @@ import net.minestom.server.entity.*;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.anvil.AnvilLoader; import net.minestom.server.instance.anvil.AnvilLoader;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set;
public class TournamentDisplay extends MineNetInstance implements Spawnable { public class TournamentDisplay extends MineNetInstance implements Spawnable {
private final List<Set<Player>> places; private final List<Player> places;
private final Tournament tournament; private final Tournament tournament;
private final Pos[] placePositions = new Pos[] { private final Pos[] placePositions = new Pos[] {
@@ -32,7 +30,7 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable {
this.places = tournament.getPlaces(); this.places = tournament.getPlaces();
this.tournament = tournament; this.tournament = tournament;
this.places.forEach(players -> players.forEach(player -> player.setGameMode(GameMode.ADVENTURE))); this.places.forEach(player -> player.setGameMode(GameMode.ADVENTURE));
eventNode().addListener(PlayerMoveEvent.class, playerMoveEvent -> { eventNode().addListener(PlayerMoveEvent.class, playerMoveEvent -> {
if(isOnDisplay(playerMoveEvent.getPlayer()) && !playerMoveEvent.getNewPosition().sameBlock(placePositions[getRankPosition(playerMoveEvent.getPlayer())])) { if(isOnDisplay(playerMoveEvent.getPlayer()) && !playerMoveEvent.getNewPosition().sameBlock(placePositions[getRankPosition(playerMoveEvent.getPlayer())])) {
@@ -47,10 +45,7 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable {
} }
private int getRankPosition(Player player) { private int getRankPosition(Player player) {
for (int i = 0; i < places.size(); i++) { return this.places.indexOf(player);
if (places.get(i).contains(player)) return i;
}
return -1;
} }
@Override @Override
@@ -61,11 +56,8 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable {
p.teleport(placePositions[getRankPosition(p)]); p.teleport(placePositions[getRankPosition(p)]);
} }
}); });
List<Player> players = this.places.stream()
.flatMap(s -> s.stream().sorted(Comparator.comparing(Player::getUsername)))
.toList();
new ChatMessage(Icon.STAR) new ChatMessage(Icon.STAR)
.list(players.stream().map(player -> String.format("%d. %s - %s Punkte", this.getRankPosition(player)+1, player.getUsername(), tournament.getGameScores().get(player))).toList()) .numberedList(this.places.stream().map(player -> String.format("%s - %s Punkte", player.getUsername(), tournament.getGameScores().get(player))).toList())
.send(p); .send(p);
return false; return false;
} }

View File

@@ -1,6 +1,9 @@
package eu.mhsl.minenet.minigames.util; package eu.mhsl.minenet.minigames.util;
import java.util.*; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class MapUtil { public class MapUtil {
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
@@ -14,16 +17,4 @@ public class MapUtil {
return result; return result;
} }
public static <K, V extends Comparable<? super V>> Map<K, V> sortReversedByValue(Map<K, V> map) {
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
list.sort(Map.Entry.<K, V>comparingByValue().reversed());
Map<K, V> result = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
} }

View File

@@ -1,28 +1,9 @@
package eu.mhsl.minenet.minigames.util; package eu.mhsl.minenet.minigames.util;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.item.Material; 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 class MaterialUtil {
public static Material fromString(String name, Material def) { public static Material fromString(String name, Material def) {
return Material.values().stream().filter(material -> material.name().equals(name)).findFirst().orElse(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}), 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}), 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}), 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, 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}); 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; final List<Block> list;

View File

@@ -21,7 +21,6 @@ public class ValeGenerator extends HeightTerrainGenerator {
.build(); .build();
private Function<Integer, Double> xShiftMultiplier = multiplier -> 1d; private Function<Integer, Double> xShiftMultiplier = multiplier -> 1d;
private Function<Integer, Double> xShiftOffset = z -> 0d;
public ValeGenerator() { public ValeGenerator() {
setCalculateHeight(this::calculateY); setCalculateHeight(this::calculateY);
@@ -33,14 +32,10 @@ public class ValeGenerator extends HeightTerrainGenerator {
} }
public int getXShiftAtZ(int z) { public int getXShiftAtZ(int z) {
return (int) ((curves.getNoise(z) * 32 + largeCurves.getNoise(z) * 64) * xShiftMultiplier.apply(z) + xShiftOffset.apply(z)); return (int) ((curves.getNoise(z) * 32 + largeCurves.getNoise(z) * 64) * xShiftMultiplier.apply(z));
} }
public void setXShiftMultiplier(Function<Integer, Double> xShiftMultiplier) { public void setXShiftMultiplier(Function<Integer, Double> xShiftMultiplier) {
this.xShiftMultiplier = xShiftMultiplier; this.xShiftMultiplier = xShiftMultiplier;
} }
public void setXShiftOffset(Function<Integer, Double> xShiftOffset) {
this.xShiftOffset = xShiftOffset;
}
} }

View File

@@ -55,19 +55,7 @@ public class HeightTerrainGenerator extends BaseGenerator {
synchronized (batches) { synchronized (batches) {
double batchNoise = batches.getNoise(bottomPoint.x(), bottomPoint.z()); double batchNoise = batches.getNoise(bottomPoint.x(), bottomPoint.z());
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), batchNoise < 0.9 ? batchNoise > 0 ? 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) { if(calculateSeaLevel != null) {
Point absoluteHeight = bottomPoint.add(0, heightModifier, 0); Point absoluteHeight = bottomPoint.add(0, heightModifier, 0);

View File

@@ -9,13 +9,6 @@ select_language;Please select your prefered Language;Bitte wähle deine bevorzug
welcome;Welcome!;Willkommen! welcome;Welcome!;Willkommen!
back;Back;Zurück back;Back;Zurück
forward;Next;Nächste 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;; ns:tablist#UNUSED;;
title;MineNet Network;MineNet Servernetzwerk title;MineNet Network;MineNet Servernetzwerk
@@ -72,11 +65,6 @@ ns:room#;;
invTitle;Select a Minigame;Wähle einen Spielmodus invTitle;Select a Minigame;Wähle einen Spielmodus
noOption;No options here;Keine Optionen hier noOption;No options here;Keine Optionen hier
noOptionDescription;There are no options for this Game;Es gibt keine Einstellungen für dieses Spiel 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#;; ns:GameFactory#;;
missingDescription;No description;Keine Beschreibung missingDescription;No description;Keine Beschreibung
@@ -90,7 +78,6 @@ ns:game_ElytraRace#;;
name;Elytra race;Elytra Rennen name;Elytra race;Elytra Rennen
description;Be fast while flying through the rings;Sei schnell während du durch die Ringe fliegst description;Be fast while flying through the rings;Sei schnell während du durch die Ringe fliegst
ringCount;ring count;Anzahl der Ringe ringCount;ring count;Anzahl der Ringe
launchIn;Launch in;Abschuss in
;; ;;
ns:game_Minerun#;; ns:game_Minerun#;;
name;Minerun;Minenrennen name;Minerun;Minenrennen
@@ -143,30 +130,3 @@ description;Run away from falling anvils;Renne von fallenden Ambossen davon
ns:game_jumpDive#;; ns:game_jumpDive#;;
name;Jump dive;Wassersprung name;Jump dive;Wassersprung
description;Jump into the water, avoiding already used spots!;Springe ins wasser an stellen, in denen noch niemand zuvor gelandet ist! description;Jump into the water, avoiding already used spots!;Springe ins wasser an stellen, in denen noch niemand zuvor gelandet ist!
;;
ns:game_Sumo#;;
name;Sumo;Sumo
lives;Lives;Leben
description;Knock your enemies off and stay on top!;Versuche deinen Gegner von der Plattform zu schubsen!
;;
ns:game_Highground#;;
name;Highground;Hochburg
description;Stay on the high ground to win!;Bleibe solange wie möglich auf der Hochburg, um zu gewinnen!
;;
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_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 114.