12 changed files with 1508 additions and 3 deletions

No files matched your search

@@ -1,5 +1,6 @@
package eu.mhsl.minenet.minigames.handler.global; package eu.mhsl.minenet.minigames.handler.global;
import eu.mhsl.minenet.minigames.Main;
import eu.mhsl.minenet.minigames.message.Icon; import eu.mhsl.minenet.minigames.message.Icon;
import eu.mhsl.minenet.minigames.message.type.ChatMessage; import eu.mhsl.minenet.minigames.message.type.ChatMessage;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -9,6 +10,9 @@ import net.minestom.server.event.player.PlayerChatEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ChatFormatHandler implements EventListener<PlayerChatEvent> { public class ChatFormatHandler implements EventListener<PlayerChatEvent> {
// Verteilt der Proxy den Chat (chat-bridge bei CraftAttack), darf er hier nicht noch einmal erscheinen
private final boolean chatViaProxy = Main.globalConfig.node("velocity", "chat-via-proxy").getBoolean(false);
@Override @Override
public @NotNull Class<PlayerChatEvent> eventType() { public @NotNull Class<PlayerChatEvent> eventType() {
return PlayerChatEvent.class; return PlayerChatEvent.class;
@@ -16,6 +20,10 @@ public class ChatFormatHandler implements EventListener<PlayerChatEvent> {
@Override @Override
public @NotNull Result run(@NotNull PlayerChatEvent event) { public @NotNull Result run(@NotNull PlayerChatEvent event) {
if(this.chatViaProxy) {
event.setCancelled(true);
return Result.SUCCESS;
}
NamedTextColor color = event.getPlayer().getPermissionLevel() > 0 ? NamedTextColor.AQUA : NamedTextColor.GRAY; NamedTextColor color = event.getPlayer().getPermissionLevel() > 0 ? NamedTextColor.AQUA : NamedTextColor.GRAY;
event.setFormattedMessage( event.setFormattedMessage(
new ChatMessage(Icon.CHAT) new ChatMessage(Icon.CHAT)
@@ -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;
@@ -43,7 +43,7 @@ public class BlockBattle extends StatelessGame {
); );
this.eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> { this.eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> {
if(this.isBeforeBeginning) finalAttackEvent.setCancelled(true); if(!this.isRunning) finalAttackEvent.setCancelled(true);
finalAttackEvent.setBaseDamage(0); finalAttackEvent.setBaseDamage(0);
((Player) finalAttackEvent.getTarget()).setHealth(20); ((Player) finalAttackEvent.getTarget()).setHealth(20);
}); });
@@ -0,0 +1,896 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
import net.minestom.server.coordinate.BlockVec;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockInteractEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.utils.Direction;
import java.util.Map;
import java.util.Set;
public final class BlockPlacementHelper {
private BlockPlacementHelper() {}
private static final Set<String> SIX_DIR = Set.of(
"observer", "dispenser", "dropper", "piston", "sticky_piston",
"amethyst_cluster", "small_amethyst_bud", "medium_amethyst_bud", "large_amethyst_bud",
"end_rod", "lightning_rod", "shulker_box"
);
public static void apply(PlayerBlockPlaceEvent event) {
Block block = event.getBlock();
BlockFace face = event.getBlockFace();
BlockVec pos = event.getBlockPosition();
Point cursor = event.getCursorPosition();
Player player = event.getPlayer();
Instance instance = event.getInstance();
String path = block.key().value();
Block existing = instance.getBlock(pos);
if ((path.endsWith("_candle") || path.equals("candle")) && existing.compare(block)) {
int n = parseIntOr(existing.getProperty("candles"), 1);
event.setBlock(n < 4 ? existing.withProperty("candles", String.valueOf(n + 1)) : existing);
return;
}
if (path.equals("snow") && existing.compare(block)) {
int n = parseIntOr(existing.getProperty("layers"), 1);
event.setBlock(n < 8 ? existing.withProperty("layers", String.valueOf(n + 1)) : existing);
return;
}
if (path.equals("sea_pickle") && existing.compare(block)) {
int n = parseIntOr(existing.getProperty("pickles"), 1);
event.setBlock(n < 4 ? existing.withProperty("pickles", String.valueOf(n + 1)) : existing);
return;
}
if (path.equals("turtle_egg") && existing.compare(block)) {
int n = parseIntOr(existing.getProperty("eggs"), 1);
event.setBlock(n < 4 ? existing.withProperty("eggs", String.valueOf(n + 1)) : existing);
return;
}
Block oriented = orient(block, path, face, cursor, player, instance, pos);
if (oriented != block) event.setBlock(oriented);
Block finalOriented = oriented;
String finalPath = finalOriented.key().value();
if (path.endsWith("_door")) {
Block lower = event.getBlock();
String facing = lower.getProperty("facing");
String hinge = lower.getProperty("hinge");
Block upper = lower.withProperty("half", "upper").withProperty("facing", facing).withProperty("hinge", hinge);
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos.add(0, 1, 0), upper));
}
if (path.endsWith("_bed")) {
String facing = finalOriented.getProperty("facing");
Direction d = facingToDirection(facing);
Block head = finalOriented.withProperty("part", "head");
BlockVec headPos = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
instance.scheduler().scheduleNextTick(() -> instance.setBlock(headPos, head));
}
if (path.equals("big_dripleaf")) {
Block upper = finalOriented;
Block lower = Block.BIG_DRIPLEAF_STEM.withProperty("facing", finalOriented.getProperty("facing"));
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos.add(0, 1, 0), upper.withProperty("half", "upper")));
}
if (isConnecting(path)) {
instance.scheduler().scheduleNextTick(() -> {
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (isConnecting(nb.key().value())) instance.setBlock(np, computeConnections(nb, nb.key().value(), instance, np));
}
Block self = instance.getBlock(pos);
if (isConnecting(self.key().value())) instance.setBlock(pos, computeConnections(self, self.key().value(), instance, pos));
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (nb.key().value().endsWith("_fence_gate")) {
instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
}
}
});
}
if (path.endsWith("_fence_gate")) {
instance.scheduler().scheduleNextTick(() -> instance.setBlock(pos, updateFenceGateInWall(instance.getBlock(pos), instance, pos)));
}
if (path.equals("redstone_wire")) {
instance.scheduler().scheduleNextTick(() -> {
updateRedstoneWire(instance, pos);
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
if (instance.getBlock(np).key().value().equals("redstone_wire")) updateRedstoneWire(instance, np);
}
});
}
if (isRail(path)) {
instance.scheduler().scheduleNextTick(() -> updateRailShape(instance, pos));
}
if (path.equals("scaffolding")) {
instance.scheduler().scheduleNextTick(() -> updateScaffolding(instance, pos));
}
if (finalPath.equals("chest") || finalPath.equals("trapped_chest")) {
instance.scheduler().scheduleNextTick(() -> updateChestPair(instance, pos));
}
}
private static Block orient(Block block, String path, BlockFace face, Point cursor, Player player,
Instance instance, BlockVec pos) {
Map<String, String> props = block.properties();
Block result = block;
if (props.containsKey("axis")) {
result = result.withProperty("axis", switch (face) {
case TOP, BOTTOM -> "y";
case NORTH, SOUTH -> "z";
case EAST, WEST -> "x";
});
}
if (props.containsKey("type") && path.endsWith("_slab")) {
Block existing = instance.getBlock(pos);
if (existing.compare(block)) {
result = result.withProperty("type", "double");
} else {
boolean top = face == BlockFace.BOTTOM || (face != BlockFace.TOP && cursor.y() > 0.5);
result = result.withProperty("type", top ? "top" : "bottom");
}
}
if (path.endsWith("_stairs")) {
String facing = yawToFacing(player.getPosition().yaw());
boolean top = face == BlockFace.BOTTOM || (face != BlockFace.TOP && cursor.y() > 0.5);
result = result.withProperty("facing", facing).withProperty("half", top ? "top" : "bottom");
result = applyStairShape(result, instance, pos, facing, top);
}
if (path.endsWith("_trapdoor")) {
String facing;
boolean top;
if (face == BlockFace.TOP) { top = false; facing = yawToFacing(player.getPosition().yaw() + 180); }
else if (face == BlockFace.BOTTOM) { top = true; facing = yawToFacing(player.getPosition().yaw() + 180); }
else { top = cursor.y() > 0.5; facing = oppositeFacing(faceName(face)); }
result = result.withProperty("facing", facing).withProperty("half", top ? "top" : "bottom")
.withProperty("open", "false").withProperty("powered", "false");
}
if (path.endsWith("_door")) {
String facing = yawToFacing(player.getPosition().yaw());
String hinge = doorHinge(facing, cursor, instance, pos, path);
result = result.withProperty("facing", facing)
.withProperty("half", "lower")
.withProperty("hinge", hinge)
.withProperty("open", "false")
.withProperty("powered", "false");
}
if (path.endsWith("_fence_gate")) {
String facing = yawToFacing(player.getPosition().yaw());
result = result.withProperty("facing", facing).withProperty("open", "false")
.withProperty("powered", "false").withProperty("in_wall", "false");
}
if (path.endsWith("_bed")) {
String facing = yawToFacing(player.getPosition().yaw());
result = result.withProperty("facing", facing).withProperty("part", "foot").withProperty("occupied", "false");
}
if (path.endsWith("_button") || path.equals("lever") || path.equals("grindstone")) {
String attachFace; String facing;
if (face == BlockFace.TOP) { attachFace = "floor"; facing = yawToFacing(player.getPosition().yaw()); }
else if (face == BlockFace.BOTTOM) { attachFace = "ceiling"; facing = yawToFacing(player.getPosition().yaw()); }
else { attachFace = "wall"; facing = oppositeFacing(faceName(face)); }
result = result.withProperty("face", attachFace).withProperty("facing", facing);
}
if (path.equals("bell")) {
String attachment; String facing;
if (face == BlockFace.TOP) { attachment = "floor"; facing = yawToFacing(player.getPosition().yaw()); }
else if (face == BlockFace.BOTTOM) { attachment = "ceiling"; facing = yawToFacing(player.getPosition().yaw()); }
else {
attachment = "single_wall";
facing = oppositeFacing(faceName(face));
}
result = result.withProperty("attachment", attachment).withProperty("facing", facing);
}
if (path.equals("lantern") || path.equals("soul_lantern")) {
result = result.withProperty("hanging", face == BlockFace.BOTTOM ? "true" : "false");
}
if (path.equals("tripwire_hook")) {
String facing = face != BlockFace.TOP && face != BlockFace.BOTTOM
? oppositeFacing(faceName(face))
: yawToFacing(player.getPosition().yaw());
result = result.withProperty("facing", facing).withProperty("attached", "false").withProperty("powered", "false");
}
if (path.equals("hopper")) {
String facing = face == BlockFace.TOP ? "down" : (face == BlockFace.BOTTOM ? "down" : oppositeFacing(faceName(face)));
result = result.withProperty("facing", facing).withProperty("enabled", "true");
}
if (path.equals("campfire") || path.equals("soul_campfire")) {
Block below = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
String signalFire = below.key().value().equals("hay_block") ? "true" : "false";
result = result.withProperty("facing", yawToFacing(player.getPosition().yaw()))
.withProperty("lit", "true").withProperty("signal_fire", signalFire);
}
if (path.equals("pointed_dripstone")) {
String dir = face == BlockFace.BOTTOM ? "down" : "up";
result = result.withProperty("vertical_direction", dir).withProperty("thickness", "tip");
}
if (path.equals("chorus_flower")) {
result = result.withProperty("age", "0");
}
if (path.equals("end_portal_frame") || path.equals("respawn_anchor")) {
if (props.containsKey("facing")) {
result = result.withProperty("facing", yawToFacing(player.getPosition().yaw()));
}
if (props.containsKey("eye")) result = result.withProperty("eye", "false");
}
if ((path.equals("torch") || path.equals("soul_torch") || path.equals("redstone_torch") || path.equals("copper_torch"))
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
Block wall = switch (path) {
case "torch" -> Block.WALL_TORCH;
case "soul_torch" -> Block.SOUL_WALL_TORCH;
case "redstone_torch" -> Block.REDSTONE_WALL_TORCH;
case "copper_torch" -> Block.COPPER_WALL_TORCH;
default -> null;
};
if (wall != null) result = wall.withProperty("facing", oppositeFacing(faceName(face)));
}
if (path.endsWith("_skull") || path.endsWith("_head")) {
if (face != BlockFace.TOP && face != BlockFace.BOTTOM) {
String wallName = path.endsWith("_skull") ? path.replace("_skull", "_wall_skull") : path.replace("_head", "_wall_head");
Block wall = Block.fromKey("minecraft:" + wallName);
if (wall != null) result = wall.withProperty("facing", oppositeFacing(faceName(face)));
} else if (face == BlockFace.TOP && result.properties().containsKey("rotation")) {
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
result = result.withProperty("rotation", String.valueOf(rot));
}
}
if (path.endsWith("_sign") && !path.endsWith("_wall_sign") && !path.contains("hanging")
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
Block wallSign = Block.fromKey("minecraft:" + path.replace("_sign", "_wall_sign"));
if (wallSign != null) result = wallSign.withProperty("facing", oppositeFacing(faceName(face)));
} else if (path.endsWith("_sign") && !path.endsWith("_wall_sign") && !path.contains("hanging")
&& face == BlockFace.TOP) {
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
}
if (path.endsWith("_hanging_sign") && !path.contains("_wall_")
&& face != BlockFace.TOP && face != BlockFace.BOTTOM) {
Block wallHang = Block.fromKey("minecraft:" + path.replace("_hanging_sign", "_wall_hanging_sign"));
if (wallHang != null) result = wallHang.withProperty("facing", oppositeFacing(faceName(face)));
} else if (path.endsWith("_hanging_sign") && !path.contains("_wall_")) {
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
if (result.properties().containsKey("attached")) result = result.withProperty("attached", face == BlockFace.BOTTOM ? "true" : "false");
}
if (isBanner(path)) {
if (face != BlockFace.TOP && face != BlockFace.BOTTOM) {
Block wallBanner = Block.fromKey("minecraft:" + path.replace("_banner", "_wall_banner"));
if (wallBanner != null) result = wallBanner.withProperty("facing", oppositeFacing(faceName(face)));
} else if (face == BlockFace.TOP) {
int rot = (int) Math.floor(((player.getPosition().yaw() + 180.0) * 16.0 / 360.0) + 0.5) & 15;
if (result.properties().containsKey("rotation")) result = result.withProperty("rotation", String.valueOf(rot));
}
}
if (path.equals("vine")) {
Block existing = instance.getBlock(pos);
Block base = existing.compare(block) ? existing : result.withProperty("up", "false")
.withProperty("north", "false").withProperty("south", "false")
.withProperty("east", "false").withProperty("west", "false");
String side = switch (face) {
case BOTTOM -> "up";
case NORTH -> "south";
case SOUTH -> "north";
case EAST -> "west";
case WEST -> "east";
default -> null;
};
result = side != null ? base.withProperty(side, "true") : base;
}
if (path.equals("glow_lichen") || path.equals("sculk_vein") || path.equals("resin_clump")) {
Block existing = instance.getBlock(pos);
Block base = existing.compare(block) ? existing : result
.withProperty("up", "false").withProperty("down", "false")
.withProperty("north", "false").withProperty("south", "false")
.withProperty("east", "false").withProperty("west", "false");
String side = switch (face) {
case TOP -> "down";
case BOTTOM -> "up";
case NORTH -> "south";
case SOUTH -> "north";
case EAST -> "west";
case WEST -> "east";
};
result = base.withProperty(side, "true");
}
if (path.equals("red_mushroom_block") || path.equals("brown_mushroom_block") || path.equals("mushroom_stem")) {
result = result.withProperty("up", "true").withProperty("down", "true")
.withProperty("north", "true").withProperty("south", "true")
.withProperty("east", "true").withProperty("west", "true");
String hiddenFace = faceName(face);
if (result.properties().containsKey(hiddenFace)) result = result.withProperty(hiddenFace, "false");
}
if (props.containsKey("facing") && !path.endsWith("_stairs") && !path.endsWith("_door")
&& !path.endsWith("_trapdoor") && !path.endsWith("_fence_gate") && !path.endsWith("_button")
&& !path.equals("lever") && !path.equals("grindstone") && !path.equals("bell")
&& !path.equals("hopper") && !path.equals("campfire") && !path.equals("soul_campfire")
&& !path.equals("tripwire_hook")
&& !path.endsWith("wall_torch") && !path.endsWith("_bed") && !path.endsWith("_sign")
&& !path.endsWith("_hanging_sign") && !path.endsWith("_banner") && !path.endsWith("_wall_banner")
&& !path.endsWith("_skull") && !path.endsWith("_head")
&& !path.equals("end_portal_frame") && !path.equals("respawn_anchor")) {
Direction fdir = faceDirection(face);
String facing;
if (fdir != null && SIX_DIR.contains(path)) {
facing = fdir.opposite().name().toLowerCase();
} else if (path.equals("repeater") || path.equals("comparator")) {
facing = oppositeFacing(yawToFacing(player.getPosition().yaw()));
} else {
facing = yawToFacing(player.getPosition().yaw());
}
try { result = result.withProperty("facing", facing); } catch (Exception ignored) {}
}
if (isConnecting(path)) result = computeConnections(result, path, instance, pos);
if (props.containsKey("waterlogged") && Block.WATER.compare(instance.getBlock(pos))) {
try { result = result.withProperty("waterlogged", "true"); } catch (Exception ignored) {}
}
return result;
}
private static boolean isBanner(String path) {
return path.endsWith("_banner") && !path.endsWith("_wall_banner");
}
private static Block applyStairShape(Block stair, Instance instance, BlockVec pos, String facing, boolean top) {
Direction f = facingToDirection(facing);
Block front = instance.getBlock(new BlockVec(pos.blockX() + f.normalX(), pos.blockY(), pos.blockZ() + f.normalZ()));
if (front.key().value().endsWith("_stairs") && top == isTopStair(front)) {
Direction frontFacing = facingToDirection(front.getProperty("facing"));
if (frontFacing != f && frontFacing != f.opposite()) {
if (frontFacing == rotateCW(f)) return stair.withProperty("shape", "outer_right");
else return stair.withProperty("shape", "outer_left");
}
}
Block back = instance.getBlock(new BlockVec(pos.blockX() - f.normalX(), pos.blockY(), pos.blockZ() - f.normalZ()));
if (back.key().value().endsWith("_stairs") && top == isTopStair(back)) {
Direction backFacing = facingToDirection(back.getProperty("facing"));
if (backFacing != f && backFacing != f.opposite()) {
if (backFacing == rotateCW(f)) return stair.withProperty("shape", "inner_right");
else return stair.withProperty("shape", "inner_left");
}
}
return stair.withProperty("shape", "straight");
}
private static boolean isTopStair(Block b) { return "top".equals(b.getProperty("half")); }
private static Block computeConnections(Block block, String path, Instance instance, BlockVec pos) {
boolean isWall = path.endsWith("_wall") && !path.endsWith("head_wall") && !path.endsWith("_wall_sign") && !path.endsWith("_wall_banner");
if (path.endsWith("_fence_gate")) return block;
Block result = block;
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
boolean connect = canConnectTo(nb, path, d);
String propName = d.name().toLowerCase();
if (isWall) {
boolean tall = connect && isSolidAbove(instance, pos);
result = result.withProperty(propName, connect ? (tall ? "tall" : "low") : "none");
} else {
result = result.withProperty(propName, connect ? "true" : "false");
}
}
if (isWall && result.properties().containsKey("up")) {
boolean up = computeWallUp(result, instance, pos);
result = result.withProperty("up", up ? "true" : "false");
}
return result;
}
private static boolean isSolidAbove(Instance instance, BlockVec pos) {
Block above = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ()));
return above.isSolid();
}
private static boolean computeWallUp(Block wall, Instance instance, BlockVec pos) {
boolean n = !"none".equals(wall.getProperty("north"));
boolean s = !"none".equals(wall.getProperty("south"));
boolean e = !"none".equals(wall.getProperty("east"));
boolean w = !"none".equals(wall.getProperty("west"));
boolean straight = (n && s && !e && !w) || (!n && !s && e && w);
if (!straight) return true;
return isSolidAbove(instance, pos);
}
private static boolean canConnectTo(Block neighbor, String selfPath, Direction toward) {
if (neighbor.isAir()) return false;
String np = neighbor.key().value();
if (np.endsWith("_fence_gate")) {
String facing = neighbor.getProperty("facing");
if (facing != null) {
Direction f = facingToDirection(facing);
return toward != f && toward != f.opposite();
}
return true;
}
if (selfPath.endsWith("_fence") && np.endsWith("_fence")) {
boolean selfNether = selfPath.contains("nether_brick");
boolean nNether = np.contains("nether_brick");
return selfNether == nNether;
}
if (selfPath.endsWith("_wall") && np.endsWith("_wall")) return true;
if ((selfPath.equals("glass_pane") || selfPath.endsWith("_stained_glass_pane") || selfPath.equals("iron_bars"))
&& (np.equals("glass_pane") || np.endsWith("_stained_glass_pane") || np.equals("iron_bars"))) return true;
return neighbor.isSolid();
}
private static boolean isConnecting(String path) {
return path.endsWith("_fence") || (path.endsWith("_wall") && !path.endsWith("head_wall")
&& !path.endsWith("_wall_sign") && !path.endsWith("_wall_banner") && !path.endsWith("_wall_torch")
&& !path.endsWith("_wall_skull") && !path.endsWith("_wall_head") && !path.endsWith("_wall_hanging_sign"))
|| path.equals("iron_bars") || path.equals("glass_pane") || path.endsWith("_stained_glass_pane");
}
private static Block updateFenceGateInWall(Block gate, Instance instance, BlockVec pos) {
if (!gate.key().value().endsWith("_fence_gate")) return gate;
String facing = gate.getProperty("facing");
if (facing == null) return gate;
Direction f = facingToDirection(facing);
Direction left = rotateCCW(f);
Direction right = rotateCW(f);
Block l = instance.getBlock(new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ()));
Block r = instance.getBlock(new BlockVec(pos.blockX() + right.normalX(), pos.blockY(), pos.blockZ() + right.normalZ()));
boolean inWall = l.key().value().endsWith("_wall") || r.key().value().endsWith("_wall");
return gate.withProperty("in_wall", inWall ? "true" : "false");
}
private static void updateRedstoneWire(Instance instance, BlockVec pos) {
Block wire = instance.getBlock(pos);
if (!wire.key().value().equals("redstone_wire")) return;
Block result = wire;
for (Direction d : Direction.HORIZONTAL) {
BlockVec side = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block sideBlock = instance.getBlock(side);
Block upBlock = instance.getBlock(new BlockVec(side.blockX(), side.blockY() + 1, side.blockZ()));
String state;
if (upBlock.key().value().equals("redstone_wire")) state = "up";
else if (isRedstoneConnectable(sideBlock)
|| instance.getBlock(new BlockVec(side.blockX(), side.blockY() - 1, side.blockZ())).key().value().equals("redstone_wire")) state = "side";
else state = "none";
result = result.withProperty(d.name().toLowerCase(), state);
}
instance.setBlock(pos, result);
}
private static boolean isRedstoneConnectable(Block b) {
String p = b.key().value();
return p.equals("redstone_wire") || p.equals("lever") || p.endsWith("_button")
|| p.equals("repeater") || p.equals("comparator") || p.equals("redstone_torch")
|| p.equals("redstone_wall_torch") || p.equals("observer") || p.equals("daylight_detector")
|| p.endsWith("_pressure_plate") || p.equals("target") || p.equals("redstone_block")
|| p.equals("tripwire_hook");
}
private static boolean isRail(String path) {
return path.equals("rail") || path.equals("powered_rail") || path.equals("detector_rail") || path.equals("activator_rail");
}
private static void updateRailShape(Instance instance, BlockVec pos) {
Block rail = instance.getBlock(pos);
String path = rail.key().value();
if (!isRail(path)) return;
boolean canCurve = path.equals("rail");
boolean[] adj = new boolean[4];
boolean[] ascAdj = new boolean[4];
Direction[] dirs = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
for (int i = 0; i < 4; i++) {
BlockVec np = new BlockVec(pos.blockX() + dirs[i].normalX(), pos.blockY(), pos.blockZ() + dirs[i].normalZ());
adj[i] = isRail(instance.getBlock(np).key().value());
BlockVec upNp = new BlockVec(np.blockX(), np.blockY() + 1, np.blockZ());
ascAdj[i] = isRail(instance.getBlock(upNp).key().value());
}
String shape;
if (canCurve) {
if (adj[0] && adj[1]) shape = "north_east";
else if (adj[1] && adj[2]) shape = "south_east";
else if (adj[2] && adj[3]) shape = "south_west";
else if (adj[3] && adj[0]) shape = "north_west";
else if (ascAdj[0]) shape = "ascending_north";
else if (ascAdj[1]) shape = "ascending_east";
else if (ascAdj[2]) shape = "ascending_south";
else if (ascAdj[3]) shape = "ascending_west";
else if (adj[1] || adj[3]) shape = "east_west";
else shape = "north_south";
} else {
if (ascAdj[0]) shape = "ascending_north";
else if (ascAdj[1]) shape = "ascending_east";
else if (ascAdj[2]) shape = "ascending_south";
else if (ascAdj[3]) shape = "ascending_west";
else if (adj[1] || adj[3]) shape = "east_west";
else shape = "north_south";
}
instance.setBlock(pos, rail.withProperty("shape", shape));
}
private static void updateScaffolding(Instance instance, BlockVec pos) {
Block s = instance.getBlock(pos);
if (!s.key().value().equals("scaffolding")) return;
Block below = instance.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
boolean bottom;
int distance;
if (below.isSolid() && !below.key().value().equals("scaffolding")) { bottom = false; distance = 0; }
else if (below.key().value().equals("scaffolding")) { bottom = false; distance = 0; }
else {
int min = 7;
for (Direction d : Direction.HORIZONTAL) {
Block n = instance.getBlock(new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ()));
if (n.key().value().equals("scaffolding")) min = Math.min(min, parseIntOr(n.getProperty("distance"), 7) + 1);
}
bottom = true;
distance = min;
}
instance.setBlock(pos, s.withProperty("bottom", String.valueOf(bottom)).withProperty("distance", String.valueOf(distance)));
}
private static void updateChestPair(Instance instance, BlockVec pos) {
Block chest = instance.getBlock(pos);
String path = chest.key().value();
if (!(path.equals("chest") || path.equals("trapped_chest"))) return;
String facing = chest.getProperty("facing");
if (facing == null) return;
Direction f = facingToDirection(facing);
Direction left = rotateCCW(f);
Direction right = rotateCW(f);
BlockVec leftPos = new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ());
BlockVec rightPos = new BlockVec(pos.blockX() + right.normalX(), pos.blockY(), pos.blockZ() + right.normalZ());
Block leftBlock = instance.getBlock(leftPos);
Block rightBlock = instance.getBlock(rightPos);
if (leftBlock.key().value().equals(path) && facing.equals(leftBlock.getProperty("facing"))
&& "single".equals(leftBlock.getProperty("type"))) {
instance.setBlock(pos, chest.withProperty("type", "right"));
instance.setBlock(leftPos, leftBlock.withProperty("type", "left"));
return;
}
if (rightBlock.key().value().equals(path) && facing.equals(rightBlock.getProperty("facing"))
&& "single".equals(rightBlock.getProperty("type"))) {
instance.setBlock(pos, chest.withProperty("type", "left"));
instance.setBlock(rightPos, rightBlock.withProperty("type", "right"));
}
}
private static String yawToFacing(float yaw) {
float y = ((yaw % 360) + 360) % 360;
if (y >= 315 || y < 45) return "south";
if (y < 135) return "west";
if (y < 225) return "north";
return "east";
}
private static String faceName(BlockFace face) {
return switch (face) {
case NORTH -> "north";
case SOUTH -> "south";
case EAST -> "east";
case WEST -> "west";
case TOP -> "up";
case BOTTOM -> "down";
};
}
private static String oppositeFacing(String f) {
return switch (f) {
case "north" -> "south";
case "south" -> "north";
case "east" -> "west";
case "west" -> "east";
case "up" -> "down";
case "down" -> "up";
default -> f;
};
}
private static Direction faceDirection(BlockFace face) {
return switch (face) {
case NORTH -> Direction.NORTH;
case SOUTH -> Direction.SOUTH;
case EAST -> Direction.EAST;
case WEST -> Direction.WEST;
case TOP -> Direction.UP;
case BOTTOM -> Direction.DOWN;
};
}
private static Direction facingToDirection(String f) {
if (f == null) return Direction.NORTH;
return switch (f) {
case "north" -> Direction.NORTH;
case "south" -> Direction.SOUTH;
case "east" -> Direction.EAST;
case "west" -> Direction.WEST;
case "up" -> Direction.UP;
case "down" -> Direction.DOWN;
default -> Direction.NORTH;
};
}
private static Direction rotateCW(Direction d) {
return switch (d) {
case NORTH -> Direction.EAST;
case EAST -> Direction.SOUTH;
case SOUTH -> Direction.WEST;
case WEST -> Direction.NORTH;
default -> d;
};
}
private static Direction rotateCCW(Direction d) {
return switch (d) {
case NORTH -> Direction.WEST;
case WEST -> Direction.SOUTH;
case SOUTH -> Direction.EAST;
case EAST -> Direction.NORTH;
default -> d;
};
}
private static String doorHinge(String facing, Point cursor, Instance instance, BlockVec pos, String path) {
Direction f = facingToDirection(facing);
Direction left = rotateCCW(f);
BlockVec leftPos = new BlockVec(pos.blockX() + left.normalX(), pos.blockY(), pos.blockZ() + left.normalZ());
Block leftBlock = instance.getBlock(leftPos);
if (leftBlock.key().value().equals(path) && facing.equals(leftBlock.getProperty("facing"))) {
return "right";
}
double x = cursor.x() - 0.5;
double z = cursor.z() - 0.5;
return switch (facing) {
case "north" -> x < 0 ? "right" : "left";
case "south" -> x > 0 ? "right" : "left";
case "west" -> z > 0 ? "right" : "left";
case "east" -> z < 0 ? "right" : "left";
default -> "left";
};
}
public static void onInteract(PlayerBlockInteractEvent event) {
Block block = event.getBlock();
String path = block.key().value();
Instance instance = event.getInstance();
BlockVec pos = event.getBlockPosition();
if (path.endsWith("_door")) {
String half = block.getProperty("half");
BlockVec lowerPos = "upper".equals(half)
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
: pos;
BlockVec upperPos = new BlockVec(lowerPos.blockX(), lowerPos.blockY() + 1, lowerPos.blockZ());
Block lower = instance.getBlock(lowerPos);
Block upper = instance.getBlock(upperPos);
if (!lower.key().value().equals(path) || !upper.key().value().equals(path)) return;
boolean open = !"true".equals(lower.getProperty("open"));
instance.setBlock(lowerPos, lower.withProperty("open", String.valueOf(open)));
instance.setBlock(upperPos, upper.withProperty("open", String.valueOf(open)));
String facing = lower.getProperty("facing");
String hinge = lower.getProperty("hinge");
if (facing != null && hinge != null) {
Direction f = facingToDirection(facing);
Direction side = "left".equals(hinge) ? rotateCW(f) : rotateCCW(f);
BlockVec partnerPos = new BlockVec(lowerPos.blockX() + side.normalX(), lowerPos.blockY(), lowerPos.blockZ() + side.normalZ());
Block partner = instance.getBlock(partnerPos);
if (partner.key().value().equals(path) && facing.equals(partner.getProperty("facing"))
&& !hinge.equals(partner.getProperty("hinge"))) {
Block partnerUpper = instance.getBlock(new BlockVec(partnerPos.blockX(), partnerPos.blockY() + 1, partnerPos.blockZ()));
instance.setBlock(partnerPos, partner.withProperty("open", String.valueOf(open)));
if (partnerUpper.key().value().equals(path)) {
instance.setBlock(new BlockVec(partnerPos.blockX(), partnerPos.blockY() + 1, partnerPos.blockZ()),
partnerUpper.withProperty("open", String.valueOf(open)));
}
}
}
event.setBlockingItemUse(true);
return;
}
if (path.endsWith("_fence_gate")) {
boolean open = !"true".equals(block.getProperty("open"));
Block updated = block.withProperty("open", String.valueOf(open));
if (open) {
String facing = block.getProperty("facing");
if (facing != null) {
Direction f = facingToDirection(facing);
Direction playerDir = horizontalFacingToDirection(yawToFacing(event.getPlayer().getPosition().yaw()));
if (playerDir == f) updated = updated.withProperty("facing", oppositeFacing(facing));
}
}
instance.setBlock(pos, updated);
event.setBlockingItemUse(true);
return;
}
if (path.endsWith("_trapdoor")) {
boolean open = !"true".equals(block.getProperty("open"));
instance.setBlock(pos, block.withProperty("open", String.valueOf(open)));
event.setBlockingItemUse(true);
}
}
private static Direction horizontalFacingToDirection(String f) {
return facingToDirection(f);
}
public static void onBreak(PlayerBlockBreakEvent event) {
Instance instance = event.getInstance();
BlockVec pos = event.getBlockPosition();
Block broken = event.getBlock();
String path = broken.key().value();
if (path.endsWith("_door")) {
String half = broken.getProperty("half");
BlockVec other = "upper".equals(half)
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
: new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
instance.scheduler().scheduleNextTick(() -> {
Block ob = instance.getBlock(other);
if (ob.key().value().equals(path)) instance.setBlock(other, Block.AIR);
});
}
if (path.endsWith("_bed")) {
String part = broken.getProperty("part");
String facing = broken.getProperty("facing");
if (facing != null) {
Direction d = facingToDirection(facing);
if ("foot".equals(part)) {
BlockVec headPos = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
instance.scheduler().scheduleNextTick(() -> {
if (instance.getBlock(headPos).key().value().equals(path)) instance.setBlock(headPos, Block.AIR);
});
} else if ("head".equals(part)) {
BlockVec footPos = new BlockVec(pos.blockX() - d.normalX(), pos.blockY(), pos.blockZ() - d.normalZ());
instance.scheduler().scheduleNextTick(() -> {
if (instance.getBlock(footPos).key().value().equals(path)) instance.setBlock(footPos, Block.AIR);
});
}
}
}
if (path.equals("big_dripleaf") || path.equals("big_dripleaf_stem")) {
String half = broken.getProperty("half");
BlockVec other = "upper".equals(half)
? new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ())
: new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
instance.scheduler().scheduleNextTick(() -> {
String op = instance.getBlock(other).key().value();
if (op.equals("big_dripleaf") || op.equals("big_dripleaf_stem")) instance.setBlock(other, Block.AIR);
});
}
instance.scheduler().scheduleNextTick(() -> {
if (isConnecting(path)) {
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (isConnecting(nb.key().value())) instance.setBlock(np, computeConnections(nb, nb.key().value(), instance, np));
if (nb.key().value().endsWith("_fence_gate")) instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
}
}
if (path.endsWith("_wall")) {
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (nb.key().value().endsWith("_fence_gate")) instance.setBlock(np, updateFenceGateInWall(nb, instance, np));
}
}
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
String npath = nb.key().value();
if (npath.equals("redstone_wire")) updateRedstoneWire(instance, np);
if (isRail(npath)) updateRailShape(instance, np);
}
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
if (instance.getBlock(above).key().value().equals("redstone_wire")) updateRedstoneWire(instance, above);
BlockVec belowRail = new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ());
if (isRail(instance.getBlock(belowRail).key().value())) updateRailShape(instance, belowRail);
if (path.equals("scaffolding") || isAnyScaffoldingAround(instance, pos)) {
recomputeScaffoldingColumn(instance, pos);
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
if (instance.getBlock(np).key().value().equals("scaffolding")) recomputeScaffoldingColumn(instance, np);
}
}
if (path.equals("chest") || path.equals("trapped_chest")) {
String facing = broken.getProperty("facing");
if (facing != null) {
Direction f = facingToDirection(facing);
for (Direction d : new Direction[]{rotateCW(f), rotateCCW(f)}) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (nb.key().value().equals(path) && facing.equals(nb.getProperty("facing"))) {
String t = nb.getProperty("type");
if ("left".equals(t) || "right".equals(t)) instance.setBlock(np, nb.withProperty("type", "single"));
}
}
}
}
if (isStairLike(path)) {
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = instance.getBlock(np);
if (nb.key().value().endsWith("_stairs")) {
String nf = nb.getProperty("facing");
boolean nt = isTopStair(nb);
instance.setBlock(np, applyStairShape(nb, instance, np, nf, nt));
}
}
}
});
}
private static boolean isStairLike(String path) { return path.endsWith("_stairs"); }
private static boolean isAnyScaffoldingAround(Instance instance, BlockVec pos) {
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
if (instance.getBlock(np).key().value().equals("scaffolding")) return true;
}
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
return instance.getBlock(above).key().value().equals("scaffolding");
}
private static void recomputeScaffoldingColumn(Instance instance, BlockVec start) {
int x = start.blockX(), z = start.blockZ();
for (int y = start.blockY(); y < start.blockY() + 32; y++) {
BlockVec p = new BlockVec(x, y, z);
if (!instance.getBlock(p).key().value().equals("scaffolding")) break;
updateScaffolding(instance, p);
}
}
private static int parseIntOr(String s, int d) {
if (s == null) return d;
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return d; }
}
}
@@ -0,0 +1,227 @@
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.coordinate.Point;
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.PlayerBlockInteractEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerUseItemEvent;
import net.minestom.server.event.player.PlayerUseItemOnBlockEvent;
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.stream.IntStream;
public class BuildBattle extends StatelessGame {
private static final int height = 100;
private static final int plotHalf = BuildBattleChunkgenerator.inner / 2;
private static final int rowCount = 5;
private static final int ratingSeconds = 15;
private static final List<Material> ratingMaterials = List.of(
Material.RED_CONCRETE, Material.ORANGE_CONCRETE, Material.YELLOW_CONCRETE,
Material.LIME_CONCRETE, Material.GREEN_CONCRETE
);
private static final List<ItemStack> ratingItems = IntStream.range(0, ratingMaterials.size())
.mapToObj(i -> ItemStack.builder(ratingMaterials.get(i))
.customName(Component.text((i + 1) + " Punkte"))
.build())
.toList();
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 int spawnPosx = 0;
private int spawnPosz = 0;
private final Map<Player, Pos> spawnPositions = new WeakHashMap<>();
private final int seconds;
private final Map<Player, Integer> currentVotes = new HashMap<>();
private Player currentOwner = null;
public BuildBattle(int seconds) {
super(Dimension.THE_END.key, "BuildBattle", new PointsWinScore());
this.seconds = seconds;
this.setGenerator(new BuildBattleChunkgenerator(height));
this.eventNode().addListener(PlayerUseItemEvent.class, this::onRatingItemUse);
this.eventNode().addListener(PlayerUseItemOnBlockEvent.class, e -> FluidFlowHelper.onBucketUse(e, p -> this.isInArea(p, e.getPlayer())));
this.eventNode().addListener(PlayerBlockInteractEvent.class, e -> {
if (this.isInArea(e.getBlockPosition(), e.getPlayer())) BlockPlacementHelper.onInteract(e);
});
}
@Override
protected void onStart() {
String theme = themes.get(this.rnd.nextInt(themes.size()));
this.showTitle(Title.title(
Component.text("Thema", NamedTextColor.GOLD),
Component.text(theme, NamedTextColor.YELLOW)
));
this.sendMessage(Component.text("Euer Thema: ", NamedTextColor.GRAY)
.append(Component.text(theme, NamedTextColor.YELLOW)));
this.getPlayers().forEach(player -> player.setGameMode(GameMode.CREATIVE));
this.scheduler().scheduleTask(this::endBuildPhase, TaskSchedule.seconds(this.seconds), TaskSchedule.stop());
this.scheduleCountdown();
}
private void scheduleCountdown() {
int[] marks = {30, 10, 5, 4, 3, 2, 1};
for (int m : marks) {
if (this.seconds <= m) continue;
int remaining = m;
this.scheduler().scheduleTask(() -> {
NamedTextColor color = remaining <= 5 ? NamedTextColor.RED : (remaining <= 10 ? NamedTextColor.GOLD : NamedTextColor.YELLOW);
this.sendMessage(Component.text("Noch ", NamedTextColor.GRAY)
.append(Component.text(remaining, color))
.append(Component.text(" Sekunde" + (remaining == 1 ? "" : "n") + " Bauzeit!", NamedTextColor.GRAY)));
}, TaskSchedule.seconds(this.seconds - m), TaskSchedule.stop());
}
}
private void endBuildPhase() {
this.sendMessage(Component.text("Die Bauzeit ist beendet!"));
this.getPlayers().forEach(p -> {
p.setGameMode(GameMode.ADVENTURE);
p.setAllowFlying(true);
p.setFlying(true);
});
this.scheduler().scheduleTask(this::rating, TaskSchedule.seconds(3), TaskSchedule.stop());
}
@Override
protected boolean onPlayerJoin(Player player) {
Pos plotCenter = new Pos(
this.spawnPosx * BuildBattleChunkgenerator.period + plotHalf,
height,
this.spawnPosz * BuildBattleChunkgenerator.period + plotHalf
);
this.spawnPositions.put(player, plotCenter);
if(this.spawnPosx >= rowCount - 1) {
this.spawnPosx = 0;
this.spawnPosz++;
} else this.spawnPosx ++;
player.setGameMode(GameMode.ADVENTURE);
this.scheduler().scheduleNextTick(() -> player.teleport(plotCenter.add(0, 1.5, 0)));
return super.onPlayerJoin(player);
}
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent event) {
if (!this.isInArea(event.getBlockPosition(), event.getPlayer())) {
event.setCancelled(true);
return;
}
BlockPlacementHelper.onBreak(event);
FluidFlowHelper.onBreak(event);
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent event) {
if (!this.isInArea(event.getBlockPosition(), event.getPlayer())) {
event.setCancelled(true);
return;
}
BlockPlacementHelper.apply(event);
FluidFlowHelper.onPlace(event);
}
private Pos safeSpawn(Player owner) {
Pos center = this.spawnPositions.get(owner);
int topY = center.blockY();
for (int y = 300; y >= center.blockY(); y--) {
if (!this.getBlock(center.blockX(), y, center.blockZ()).isAir()) {
topY = y;
break;
}
}
return new Pos(center.x(), topY + 3, center.z()).withView(center.yaw(), center.pitch());
}
private void rating() {
Queue<Player> owners = new ArrayDeque<>(this.getPlayers());
this.scheduler().submitTask(() -> {
if (this.currentOwner != null) this.finishRound();
Player next = owners.poll();
while (next != null && !(next.isOnline() && this.spawnPositions.containsKey(next))) next = owners.poll();
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.safeSpawn(owner);
this.sendMessage(Component.text("Bewerte das Grundstück von " + owner.getUsername() + "!"));
this.getPlayers().forEach(p -> {
p.teleport(plot);
p.getInventory().clear();
if (p != owner) this.giveRatingItems(p);
});
}
private void giveRatingItems(Player player) {
for (int i = 0; i < ratingItems.size(); i++) {
player.getInventory().setItemStack(i, ratingItems.get(i));
}
}
private void onRatingItemUse(PlayerUseItemEvent event) {
Player voter = event.getPlayer();
if (this.currentOwner == null || voter == this.currentOwner) return;
int slot = voter.getHeldSlot();
if (slot >= ratingItems.size()) return;
event.setCancelled(true);
this.currentVotes.put(voter, slot + 1);
voter.sendMessage(Component.text("Du hast " + (slot + 1) + " Punkte vergeben."));
}
private void finishRound() {
int voteSum = this.currentVotes.values().stream()
.mapToInt(Integer::intValue)
.sum();
int score = (int) Math.round((double) voteSum / this.getPlayers().size());
this.getPlayers().forEach(p -> p.getInventory().clear());
this.getScore().insertResult(this.currentOwner, score);
this.currentOwner = null;
}
private boolean isInArea(Point block, Player player) {
Pos center = this.spawnPositions.get(player);
if (center == null) return false;
Pos min = new Pos(center.x() - plotHalf, center.y() + 1, center.z() - plotHalf);
Pos max = new Pos(center.x() + plotHalf - 1, 300, center.z() + plotHalf - 1);
return eu.mhsl.minenet.minigames.util.Position.isInArea(block, min, max);
}
}
@@ -0,0 +1,41 @@
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 {
static final int inner = 32;
private static final int ring = 3;
private static final int wall = 1;
private static final int wallHeight = 200;
static final int period = wall + ring + inner + ring;
private static final int innerStart = wall + ring;
private static final int innerEnd = innerStart + inner;
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() + innerStart, period);
int lz = Math.floorMod(point.blockZ() + innerStart, period);
if (lx < wall || lz < wall) {
unit.modifier().fill(point.withY(height), point.add(1, 0, 1).withY(height + wallHeight + 1), wallBlocks);
} else {
unit.modifier().setBlock(point.withY(height), floorBlock(lx, lz));
}
}));
}
private static Block floorBlock(int lx, int lz) {
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");
}
}
@@ -0,0 +1,267 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.buildBattle;
import net.minestom.server.coordinate.BlockVec;
import net.minestom.server.coordinate.Point;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerUseItemOnBlockEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.timer.TaskSchedule;
import net.minestom.server.utils.Direction;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
public final class FluidFlowHelper {
private FluidFlowHelper() {}
private static final int WATER_TICK = 5;
private static final int LAVA_TICK = 30;
private static final int WATER_RANGE = 7;
private static final int LAVA_RANGE = 3;
private static final int FALLING = 8;
public static void onBucketUse(PlayerUseItemOnBlockEvent event, Predicate<Point> inArea) {
Material m = event.getItemStack().material();
Instance inst = event.getPlayer().getInstance();
if (inst == null) return;
Point hit = event.getPosition();
var face = event.getBlockFace();
BlockVec placePos = new BlockVec(
hit.blockX() + face.toDirection().normalX(),
hit.blockY() + face.toDirection().normalY(),
hit.blockZ() + face.toDirection().normalZ()
);
if (!inArea.test(placePos)) return;
if (m == Material.WATER_BUCKET) {
inst.setBlock(placePos, Block.WATER.withProperty("level", "0"));
scheduleFlow(inst, placePos, true);
} else if (m == Material.LAVA_BUCKET) {
inst.setBlock(placePos, Block.LAVA.withProperty("level", "0"));
scheduleFlow(inst, placePos, false);
} else if (m == Material.BUCKET) {
Block existing = inst.getBlock(placePos);
if (isWater(existing) || isLava(existing)) inst.setBlock(placePos, Block.AIR);
}
}
public static void onPlace(PlayerBlockPlaceEvent event) {
Block b = event.getBlock();
if (isWater(b) || isLava(b)) {
Instance inst = event.getInstance();
BlockVec pos = event.getBlockPosition();
scheduleFlow(inst, pos, isWater(b));
}
triggerNeighbors(event.getInstance(), event.getBlockPosition());
}
public static void onBreak(PlayerBlockBreakEvent event) {
triggerNeighbors(event.getInstance(), event.getBlockPosition());
}
private static void triggerNeighbors(Instance inst, BlockVec pos) {
for (Direction d : Direction.values()) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY() + d.normalY(), pos.blockZ() + d.normalZ());
Block nb = inst.getBlock(np);
if (isWater(nb) || isLava(nb)) scheduleFlow(inst, np, isWater(nb));
}
}
private static void scheduleFlow(Instance inst, BlockVec pos, boolean water) {
int delay = water ? WATER_TICK : LAVA_TICK;
inst.scheduler().buildTask(() -> step(inst, pos, water)).delay(TaskSchedule.tick(delay)).schedule();
}
private static void step(Instance inst, BlockVec pos, boolean water) {
Block self = inst.getBlock(pos);
if (water ? !isWater(self) : !isLava(self)) return;
int level = parseLevel(self);
boolean source = level == 0;
boolean falling = level >= FALLING;
int flowLevel = source ? 1 : (falling ? 1 : level + 1);
int maxRange = water ? WATER_RANGE : LAVA_RANGE;
if (!source && !falling) {
int newLevel = computeIncomingLevel(inst, pos, water, maxRange);
if (newLevel == -1) {
inst.setBlock(pos, Block.AIR);
triggerNeighbors(inst, pos);
return;
}
if (newLevel != level) {
inst.setBlock(pos, (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(newLevel)));
level = newLevel;
flowLevel = level + 1;
}
}
if (water && !source && countAdjacentSources(inst, pos, true) >= 2
&& hasSolidBelow(inst, pos)) {
inst.setBlock(pos, Block.WATER.withProperty("level", "0"));
scheduleFlow(inst, pos, true);
triggerNeighbors(inst, pos);
return;
}
BlockVec below = new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ());
Block belowBlock = inst.getBlock(below);
if (isReplaceable(belowBlock) || (water ? isWater(belowBlock) : isLava(belowBlock))) {
if (handleFluidCollision(inst, below, water)) {
triggerNeighbors(inst, below);
return;
}
Block target = (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(FALLING));
if (!belowBlock.compare(target) || !target.getProperty("level").equals(belowBlock.getProperty("level"))) {
if (!(isWater(belowBlock) && parseLevel(belowBlock) == 0)
&& !(isLava(belowBlock) && parseLevel(belowBlock) == 0)) {
inst.setBlock(below, target);
scheduleFlow(inst, below, water);
triggerNeighbors(inst, below);
}
}
if (!hasSolidBelow(inst, pos) && !source) return;
}
if (flowLevel > maxRange) return;
Set<Direction> targets = findFlowTargets(inst, pos, water, maxRange - (source ? 0 : level));
for (Direction d : Direction.HORIZONTAL) {
if (!targets.contains(d)) continue;
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = inst.getBlock(np);
if (handleFluidCollision(inst, np, water)) continue;
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) continue;
int existingLevel = (water ? isWater(nb) : isLava(nb)) ? parseLevel(nb) : Integer.MAX_VALUE;
if (existingLevel == 0) continue;
if (flowLevel < existingLevel) {
inst.setBlock(np, (water ? Block.WATER : Block.LAVA).withProperty("level", String.valueOf(flowLevel)));
scheduleFlow(inst, np, water);
triggerNeighbors(inst, np);
}
}
}
private static int computeIncomingLevel(Instance inst, BlockVec pos, boolean water, int maxRange) {
int min = Integer.MAX_VALUE;
BlockVec above = new BlockVec(pos.blockX(), pos.blockY() + 1, pos.blockZ());
Block ab = inst.getBlock(above);
if ((water ? isWater(ab) : isLava(ab))) return FALLING;
for (Direction d : Direction.HORIZONTAL) {
BlockVec np = new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ());
Block nb = inst.getBlock(np);
if (water ? isWater(nb) : isLava(nb)) {
int nl = parseLevel(nb);
if (nl >= FALLING) nl = 0;
if (nl < min) min = nl;
}
}
if (min == Integer.MAX_VALUE) return -1;
int result = min + 1;
if (result > maxRange) return -1;
return result;
}
private static Set<Direction> findFlowTargets(Instance inst, BlockVec pos, boolean water, int remaining) {
Set<Direction> result = new HashSet<>();
if (remaining <= 0) return result;
int bestDist = Integer.MAX_VALUE;
int[] dist = new int[4];
Direction[] dirs = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
for (int i = 0; i < 4; i++) {
BlockVec np = new BlockVec(pos.blockX() + dirs[i].normalX(), pos.blockY(), pos.blockZ() + dirs[i].normalZ());
Block nb = inst.getBlock(np);
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) { dist[i] = Integer.MAX_VALUE; continue; }
dist[i] = bfsDownhill(inst, np, water, remaining);
if (dist[i] < bestDist) bestDist = dist[i];
}
if (bestDist == Integer.MAX_VALUE) {
for (int i = 0; i < 4; i++) {
if (dist[i] < Integer.MAX_VALUE) result.add(dirs[i]);
}
} else {
for (int i = 0; i < 4; i++) {
if (dist[i] == bestDist) result.add(dirs[i]);
}
}
return result;
}
private static int bfsDownhill(Instance inst, BlockVec start, boolean water, int remaining) {
ArrayDeque<int[]> queue = new ArrayDeque<>();
Set<Long> visited = new HashSet<>();
queue.add(new int[]{start.blockX(), start.blockY(), start.blockZ(), 0});
visited.add(key(start.blockX(), start.blockY(), start.blockZ()));
while (!queue.isEmpty()) {
int[] c = queue.poll();
BlockVec cp = new BlockVec(c[0], c[1], c[2]);
Block below = inst.getBlock(new BlockVec(c[0], c[1] - 1, c[2]));
if (isReplaceable(below) && !(water ? isWater(below) : isLava(below))) return c[3];
if (c[3] >= remaining) continue;
for (Direction d : Direction.HORIZONTAL) {
int nx = c[0] + d.normalX(), nz = c[2] + d.normalZ();
long k = key(nx, c[1], nz);
if (visited.contains(k)) continue;
Block nb = inst.getBlock(new BlockVec(nx, c[1], nz));
if (!isReplaceable(nb) && !(water ? isWater(nb) : isLava(nb))) continue;
visited.add(k);
queue.add(new int[]{nx, c[1], nz, c[3] + 1});
}
}
return Integer.MAX_VALUE;
}
private static long key(int x, int y, int z) {
return ((long) x & 0x3FFFFFFL) | (((long) z & 0x3FFFFFFL) << 26) | (((long) y & 0xFFFL) << 52);
}
private static int countAdjacentSources(Instance inst, BlockVec pos, boolean water) {
int c = 0;
for (Direction d : Direction.HORIZONTAL) {
Block nb = inst.getBlock(new BlockVec(pos.blockX() + d.normalX(), pos.blockY(), pos.blockZ() + d.normalZ()));
if ((water ? isWater(nb) : isLava(nb)) && parseLevel(nb) == 0) c++;
}
return c;
}
private static boolean hasSolidBelow(Instance inst, BlockVec pos) {
Block below = inst.getBlock(new BlockVec(pos.blockX(), pos.blockY() - 1, pos.blockZ()));
return below.isSolid();
}
private static boolean handleFluidCollision(Instance inst, BlockVec pos, boolean incomingIsWater) {
Block target = inst.getBlock(pos);
if (incomingIsWater && isLava(target)) {
boolean lavaSource = parseLevel(target) == 0;
inst.setBlock(pos, lavaSource ? Block.OBSIDIAN : Block.COBBLESTONE);
return true;
}
if (!incomingIsWater && isWater(target)) {
inst.setBlock(pos, Block.STONE);
return true;
}
return false;
}
private static boolean isReplaceable(Block b) {
if (b.isAir()) return true;
String p = b.key().value();
return p.equals("short_grass") || p.equals("tall_grass") || p.equals("fern") || p.equals("large_fern")
|| p.equals("dead_bush") || p.equals("seagrass") || p.equals("tall_seagrass") || p.equals("vine")
|| p.equals("snow") || p.equals("fire") || p.equals("soul_fire");
}
private static boolean isWater(Block b) { return b.compare(Block.WATER); }
private static boolean isLava(Block b) { return b.compare(Block.LAVA); }
private static int parseLevel(Block b) {
String s = b.getProperty("level");
if (s == null) return 0;
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return 0; }
}
}
@@ -172,5 +172,9 @@ public class ColorJump extends StatelessGame {
this.getScore().insertResult(player); this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR); player.setGameMode(GameMode.SPECTATOR);
} }
if(!this.isBeforeBeginning && !this.isRunning) {
player.setGameMode(GameMode.SPECTATOR);
player.teleport(this.getSpawn());
}
} }
} }
@@ -44,4 +44,17 @@ public class Position {
public static boolean hasPositionChanged(Pos oldPos, Pos newPos) { public static boolean hasPositionChanged(Pos oldPos, Pos newPos) {
return !oldPos.withView(0, 0).equals(newPos.withView(0, 0)); return !oldPos.withView(0, 0).equals(newPos.withView(0, 0));
} }
public static boolean isInArea(Point point, Point pos1, Point pos2) {
double minX = Math.min(pos1.x(), pos2.x());
double maxX = Math.max(pos1.x(), pos2.x());
double minY = Math.min(pos1.y(), pos2.y());
double maxY = Math.max(pos1.y(), pos2.y());
double minZ = Math.min(pos1.z(), pos2.z());
double maxZ = Math.max(pos1.z(), pos2.z());
return point.x() >= minX && point.x() <= maxX
&& point.y() >= minY && point.y() <= maxY
&& point.z() >= minZ && point.z() <= maxZ;
}
} }
+2
View File
@@ -9,6 +9,8 @@ bungeecord:
velocity: velocity:
enabled: false enabled: false
# Chat nur über den Proxy verteilen, hier verwerfen (chat-bridge)
chat-via-proxy: false
secret: '' secret: ''
api: api:
+6 -1
View File
@@ -190,4 +190,9 @@ 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
190
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