added Build Battle #15

Merged
jannis merged 6 commits from develop-jannis into develop 2026-10-16 18:50:20 +00:00
5 changed files with 303 additions and 1 deletions
Showing only changes of commit 7e0c126a0d - Show all commits

No files matched your search

@@ -9,6 +9,7 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBattle.Block
import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace.BlockBreakRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace.BlockBreakRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.boatRace.BoatRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.boatRace.BoatRaceFactory;
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.buildBattle.BuildBattleFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.colorJump.ColorJumpFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.colorJump.ColorJumpFactory;
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.elytraRace.ElytraRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.elytraRace.ElytraRaceFactory;
@@ -54,7 +55,8 @@ public enum GameList {
PILLARS(new PillarsFactory(), GameType.PROTOTYPE), PILLARS(new PillarsFactory(), GameType.PROTOTYPE),
BLOCKBATTLE(new BlockBattleFactory(), GameType.PVP), BLOCKBATTLE(new BlockBattleFactory(), GameType.PVP),
COLORJUMP(new ColorJumpFactory(), GameType.JUMPNRUN), COLORJUMP(new ColorJumpFactory(), GameType.JUMPNRUN),
PLAYERRACE(new PlayerRaceFactory(), GameType.JUMPNRUN); PLAYERRACE(new PlayerRaceFactory(), GameType.JUMPNRUN),
BUILDBATTLE(new BuildBattleFactory(), GameType.PROTOTYPE);
private final GameFactory factory; private final GameFactory factory;
private final GameType type; private final GameType type;
@@ -0,0 +1,205 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.PointsWinScore;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.Title;
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.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerUseItemEvent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class BuildBattle extends StatelessGame {
private static final int height = 100;
private int spawnPosx = 0;
private int spawnPosz = 0;
private final int spacing = 39;
private final int rowCount = 5;
private Map<Player, Pos> spawnPositions = new WeakHashMap<>();
private int seconds;
private static final int ratingSeconds = 15;
private final Map<Player, Integer> scores = new HashMap<>();
private final Map<Player, Integer> currentVotes = new HashMap<>();
private Player currentOwner = null;
private boolean listenerRegistered = false;
private static final List<String> themes = List.of(
"Burg", "Raumschiff", "Unterwasserwelt", "Pizzeria", "Spukhaus",
"Bauernhof", "Dschungel", "Zukunftsstadt", "Piratenschiff", "Vulkan",
"Freizeitpark", "Leuchtturm", "Wikingerdorf", "Weihnachtsmarkt",
"Schloss", "Strand", "Berghütte", "Bahnhof", "Zoo", "Schule",
"Zeitreise", "Ein Haus, das auf dem Kopf steht", "Eine Stadt auf dem Rücken eines Riesen",
"Wohnen in einer Schneekugel", "Alles ist aus Süßigkeiten", "Ein Schiff in der Luft",
"Das Museum der Zukunft", "Das letzte Haus vor dem Weltuntergang",
"Wenn Schweine fliegen könnten", "Das Gefängnis für Creeper", "Die Wohnung von Herobrine",
"Ein Dönerladen im Weltall", "Ein Spa für Enderman", "Der Käse-Palast",
"Mein Zimmer nach 3 Wochen ohne Aufräumen", "Die Hüpfburg des Todes"
);
private String theme = "";
public BuildBattle(int seconds) {
super(Dimension.THE_END.key, "BluidBattle", new PointsWinScore());
this.seconds = seconds;
this.setGenerator(new BuildBattleChunkgenerator(height));
}
@Override
protected void onStart() {
this.theme = themes.get(this.rnd.nextInt(themes.size()));
Title title = Title.title(
Component.text("Thema", NamedTextColor.GOLD),
Component.text(this.theme, NamedTextColor.YELLOW)
);
this.getPlayers().forEach(player -> {
player.showTitle(title);
player.sendMessage(Component.text("Euer Thema: ", NamedTextColor.GRAY)
.append(Component.text(this.theme, NamedTextColor.YELLOW)));
});
this.getPlayers().forEach(player -> player.setGameMode(GameMode.CREATIVE));
MinecraftServer.getSchedulerManager()
.buildTask(() -> {
this.getPlayers().forEach(player -> player.sendMessage("Die Bauzeit ist beendet!"));
this.getPlayers().forEach(p -> {
p.setGameMode(GameMode.ADVENTURE);
p.setAllowFlying(true);
p.setFlying(true);
this.scores.put(p, 0);
});
MinecraftServer.getSchedulerManager()
.buildTask(() -> {
this.rating();
}).delay(TaskSchedule.seconds(3)).schedule();
}).delay(TaskSchedule.seconds(this.seconds)).schedule();
}
@Override
protected boolean onPlayerJoin(Player player) {
Pos spawnPos = new Pos(this.spawnPosx * this.spacing, height, this.spawnPosz * this.spacing);
this.spawnPositions.put(player, spawnPos.add(16, 0, 16));
if(this.spawnPosx >= this.rowCount) {
this.spawnPosx = 0;
this.spawnPosz++;
}
this.spawnPosx++;
player.setGameMode(GameMode.ADVENTURE);
MinecraftServer.getSchedulerManager().scheduleNextTick(() ->
player.teleport(spawnPos.add(16, 1.5, 16)));
return super.onPlayerJoin(player);
}
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
var player = event.getPlayer();
Pos blockPos = event.getBlockPosition().asPos();
event.setCancelled(!this.isInArea(blockPos, player));
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent event) {
var player = event.getPlayer();
Pos blockPos = event.getBlockPosition().asPos();
event.setCancelled(!this.isInArea(blockPos, player));
}
private void rating() {
List<Player> owners = new ArrayList<>(this.getPlayers());
this.registerRatingListener();
AtomicInteger index = new AtomicInteger(0);
MinecraftServer.getSchedulerManager().submitTask(() -> {
if (this.currentOwner != null) this.finishRound();
Player next = null;
while (index.get() < owners.size()) {
Player candidate = owners.get(index.getAndIncrement());
if (candidate.isOnline() && this.spawnPositions.containsKey(candidate)) {
next = candidate;
break;
}
}
if (next == null) {
return TaskSchedule.stop();
}
this.startRound(next);
return TaskSchedule.seconds(ratingSeconds);
});
}
private void startRound(Player owner) {
this.currentOwner = owner;
this.currentVotes.clear();
Pos plot = this.spawnPositions.get(owner).add(0, 1.5, 0);
this.getPlayers().forEach(p -> {
p.teleport(plot);
p.sendMessage(Component.text("Bewerte das Grundstück von " + owner.getUsername() + "!"));
if (p != owner) this.giveRatingItems(p);
else p.getInventory().clear();
});
}
private void giveRatingItems(Player player) {
Material[] materials = {
Material.RED_CONCRETE, Material.ORANGE_CONCRETE, Material.YELLOW_CONCRETE,
Material.LIME_CONCRETE, Material.GREEN_CONCRETE
};
player.getInventory().clear();
for (int i = 0; i < materials.length; i++) {
player.getInventory().setItemStack(i, ItemStack.builder(materials[i])
.customName(Component.text((i + 1) + " Punkte"))
.build());
}
}
private void registerRatingListener() {
if (this.listenerRegistered) return;
this.listenerRegistered = true;
this.eventNode().addListener(PlayerUseItemEvent.class, event -> {
Player voter = event.getPlayer();
if (this.currentOwner == null || voter == this.currentOwner) return;
int slot = voter.getHeldSlot();
if (slot > 4) return;
event.setCancelled(true);
this.currentVotes.put(voter, slot + 1);
voter.sendMessage(Component.text("Du hast " + (slot + 1) + " Punkte vergeben."));
});
}
private void finishRound() {
int sum = this.currentVotes.values().stream().mapToInt(Integer::intValue).sum();
this.getPlayers().forEach(p -> p.getInventory().clear());
Player owner = this.currentOwner;
this.currentOwner = null;
this.getScore().insertResult(owner, sum);
}
private boolean isInArea(Pos blockToCheck, Player player) {
Pos pos1 = this.spawnPositions.get(player).add(15, 1, 15);
Pos pos2 = this.spawnPositions.get(player).add(-16, 300, -16);
return blockToCheck.x() >= Math.min(pos1.x(), pos2.x()) && blockToCheck.x() <= Math.max(pos1.x(), pos2.x())
&& blockToCheck.y() >= Math.min(pos1.y(), pos2.y()) && blockToCheck.y() <= Math.max(pos1.y(), pos2.y())
&& blockToCheck.z() >= Math.min(pos1.z(), pos2.z()) && blockToCheck.z() <= Math.max(pos1.z(), pos2.z());
}
}
@@ -0,0 +1,50 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
import eu.mhsl.minenet.minigames.util.GeneratorUtils;
import eu.mhsl.minenet.minigames.world.generator.terrain.BaseGenerator;
import net.minestom.server.instance.block.Block;
public class BuildBattleChunkgenerator extends BaseGenerator {
private static final int inner = 32;
private static final int ring = 3;
private static final int wall = 1;
private static final int wallHeight = 200;
private static final int period = wall + ring + inner + ring;
private static final int offset = wall + ring;
private static final Block middleBlocks = Block.QUARTZ_BLOCK;
private static final Block outerBlocks = Block.STONE_BRICKS;
private static final Block wallBlocks = Block.POLISHED_DEEPSLATE;
public BuildBattleChunkgenerator(int height) {
this.addMixIn(unit -> {
GeneratorUtils.foreachXZ(unit, point -> {
int lx = Math.floorMod(point.blockX() + offset, period);
int lz = Math.floorMod(point.blockZ() + offset, period);
boolean isWall = lx < wall || lz < wall;
if (isWall) {
for (int y = 0; y <= wallHeight; y++) {
unit.modifier().setBlock(point.withY(height + y), wallBlocks);
}
return;
}
unit.modifier().setBlock(point.withY(height), floorBlock(lx, lz));
});
});
}
private static Block floorBlock(int lx, int lz) {
int innerStart = wall + ring;
int innerEnd = innerStart + inner;
boolean inInner = lx >= innerStart && lx < innerEnd
&& lz >= innerStart && lz < innerEnd;
return inInner ? middleBlocks : outerBlocks;
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
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 BuildBattleFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_BuildBattle#name");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("seconds", Material.CLOCK, TranslatedComponent.byId("game_BuildBattle#seconds"), 60, 120, 300, 600));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new BuildBattle(configuration.get("seconds").getAsInt()).setParent(parent);
}
@Override
public Material symbol() {
return Material.SANDSTONE;
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_BuildBattle#description");
}
}
+5
View File
@@ -191,3 +191,8 @@ description;Jump on the right color!;Springe auf die richtige Farbe!
ns:game_PlayerRace#;; ns:game_PlayerRace#;;
name;Player Race;Wettrennen name;Player Race;Wettrennen
description;Get to the other side as quickly as possible!;Komme so schnell wie möglich auf die andere Seite! description;Get to the other side as quickly as possible!;Komme so schnell wie möglich auf die andere Seite!
;;
ns:game_BuildBattle#;;
name;Build Battle;Bauwettbewerb
description;Build the best creation for the given theme before time runs out and win through the other players’ ratings.;Baue in der Zeit das beste Bauwerk zum Thema und gewinne durch die Bewertung der anderen Spieler.
seconds;Build Time;Bauzeit
1 map en_us de_de
191 ns:game_PlayerRace#
192 name Player Race Wettrennen
193 description Get to the other side as quickly as possible! Komme so schnell wie möglich auf die andere Seite!
194
195 ns:game_BuildBattle#
196 name Build Battle Bauwettbewerb
197 description Build the best creation for the given theme before time runs out and win through the other players’ ratings. Baue in der Zeit das beste Bauwerk zum Thema und gewinne durch die Bewertung der anderen Spieler.
198 seconds Build Time Bauzeit