From b2ddc1ef2d73043f10fa5f3e8e932b0bbe70bd3e Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 22 Feb 2023 18:52:30 +0100 Subject: [PATCH] Add KillcheckerCommand and KillcheckerVisualizer Signed-off-by: yoyosource --- BauSystem_Main/src/BauSystem.properties | 7 + BauSystem_Main/src/BauSystem_de.properties | 7 + .../killchecker/KillcheckerCommand.java | 102 +++++++++ .../killchecker/KillcheckerVisualizer.java | 216 ++++++++++++++++++ build.gradle | 6 - 5 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java diff --git a/BauSystem_Main/src/BauSystem.properties b/BauSystem_Main/src/BauSystem.properties index 690fcb6d..8d03d0ca 100644 --- a/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem_Main/src/BauSystem.properties @@ -651,6 +651,13 @@ INVENTORY_FILL_INFO = §7Helps you fill containers by looking at them while snea INVENTORY_FILL_ENABLE = §aInventoryFiller activated INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated +# Killchecker +KILLCHECKER_HELP_ENABLE = §8/§ekillchecker enable §8- §7Enables Killchecker / Recalculates kills +KILLCHECKER_HELP_DISABLE = §8/§ekillchecker disable §8- §7Disables Killchecker +KILLCHECKER_INFO = §7Shows the overlaps of cannon kills in your build area. +KILLCHECKER_ENABLE = §aKillchecker activated +KILLCHECKER_DISABLE = §cKillchecker deactivated + # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Toggle on/off BLOCK_COUNTER_HELP_ENABLE = §8/§eblockcounter enable §8- §7Toggles BlockCounter on diff --git a/BauSystem_Main/src/BauSystem_de.properties b/BauSystem_Main/src/BauSystem_de.properties index 7320b18d..f49f1fc6 100644 --- a/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem_Main/src/BauSystem_de.properties @@ -624,6 +624,13 @@ INVENTORY_FILL_INFO = §7Hilft dir, Behälter zu füllen, indem du sie beim snea INVENTORY_FILL_ENABLE = §aInventoryFiller activated INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated +# Killchecker +KILLCHECKER_HELP_ENABLE = §8/§ekillchecker enable §8- §7Aktiviert Killchecker / Berechnet kills neu +KILLCHECKER_HELP_DISABLE = §8/§ekillchecker disable §8- §7Deaktiviert Killchecker +KILLCHECKER_INFO = §7Zeigt Überlappungen der Kanonen Kills im Baubereich an. +KILLCHECKER_ENABLE = §aKillchecker aktiviert +KILLCHECKER_DISABLE = §cKillchecker deaktiviert + # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Wechsel zwischen an und aus BLOCK_COUNTER_HELP_ENABLE = §8/§eblockcounter enable §8- §7Schalte den BlockCounter an diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java new file mode 100644 index 00000000..fee15416 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java @@ -0,0 +1,102 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.killchecker; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Region; +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Linked +public class KillcheckerCommand extends SWCommand implements Listener { + + private Map visualizers = new HashMap<>(); + + public KillcheckerCommand() { + super("killchecker"); + addDefaultHelpMessage("KILLCHECKER_INFO"); + } + + @Register(value = "enable", description = "KILLCHECKER_HELP_ENABLE") + public void genericCommand(Player player) { + Region region = Region.getRegion(player.getLocation()); + KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, KillcheckerVisualizer::new); + killcheckerVisualizer.recalc(); + killcheckerVisualizer.show(player); + BauSystem.MESSAGE.send("KILLCHECKER_ENABLE", player); + } + + @Register(value = "disable", description = "KILLCHECKER_HELP_DISABLE") + public void disableCommand(Player player) { + Region region = Region.getRegion(player.getLocation()); + KillcheckerVisualizer killcheckerVisualizer = visualizers.get(region); + if (killcheckerVisualizer != null) { + if (killcheckerVisualizer.hide(player)) { + visualizers.remove(region); + } + } + BauSystem.MESSAGE.send("KILLCHECKER_DISABLE", player); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + Set regions = new HashSet<>(); + visualizers.forEach((region, visualizer) -> { + if (visualizer.hide(player)) { + regions.add(region); + } + }); + regions.forEach(visualizers::remove); + } + + private void recalc(Block block) { + Region region = Region.getRegion(block.getLocation()); + KillcheckerVisualizer killcheckerVisualizer = visualizers.get(region); + if (killcheckerVisualizer != null) { + killcheckerVisualizer.recalc(); + } + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + recalc(event.getBlock()); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + recalc(event.getBlock()); + }, 1); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java new file mode 100644 index 00000000..168b1893 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java @@ -0,0 +1,216 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.killchecker; + +import de.steamwar.bausystem.features.slaves.laufbau.Cuboid; +import de.steamwar.bausystem.region.Point; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.region.utils.RegionExtensionType; +import de.steamwar.bausystem.region.utils.RegionType; +import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityServer; +import de.steamwar.entity.RFallingBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.util.*; + +public class KillcheckerVisualizer { + + private static final Material[] materials = new Material[] {Material.YELLOW_STAINED_GLASS, Material.ORANGE_STAINED_GLASS, Material.RED_STAINED_GLASS, Material.PURPLE_STAINED_GLASS, Material.BLACK_STAINED_GLASS}; + private static final World WORLD = Bukkit.getWorlds().get(0); + + private Point minPoint; + private Point maxPoint; + + private Set players = new HashSet<>(); + + public KillcheckerVisualizer(Region region) { + this.minPoint = region.getMinPoint(RegionType.BUILD, RegionExtensionType.NORMAL); + this.maxPoint = region.getMaxPoint(RegionType.BUILD, RegionExtensionType.NORMAL); + } + + private REntityServer rEntityServer = new REntityServer(); + + private Map killCount = new HashMap<>(); + private Map rEntities = new HashMap<>(); + + public void recalc() { + Set cuboids = new HashSet<>(); + Set points = new HashSet<>(); + for (int x = minPoint.getX() + 1; x < maxPoint.getX() - 1; x++) { + for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { + for (int z = minPoint.getZ() + 1; z < maxPoint.getZ() - 1; z++) { + if (points.contains(new Point(x, y, z))) continue; + Block block = WORLD.getBlockAt(x, y, z); + if (block.getType().isAir()) continue; + Cuboid cuboid = create(block.getType(), x, y, z); + cuboids.add(cuboid); + for (int dx = (int) cuboid.getX(); dx <= cuboid.getDx(); dx++) { + for (int dy = (int) cuboid.getY(); dy <= cuboid.getDy(); dy++) { + for (int dz = (int) cuboid.getZ(); dz <= cuboid.getDz(); dz++) { + points.add(new Point(dx, dy, dz)); + } + } + } + } + } + } + + Map kill = new HashMap<>(); + for (int x = minPoint.getX(); x < maxPoint.getX(); x++) { + for (int z = minPoint.getZ(); z < maxPoint.getZ(); z++) { + Set cuboidSet = new HashSet<>(); + for (Cuboid cuboid : cuboids) { + if (x >= cuboid.getX() - 3.5 && x <= cuboid.getDx() + 3.5 && z >= cuboid.getZ() - 3.5 && z <= cuboid.getDz() + 3.5) { + cuboidSet.add(cuboid); + } + } + if (cuboidSet.size() > 1) { + Point p1 = new Point(x, minPoint.getY(), z); + kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); + Point p2 = new Point(x, maxPoint.getY(), z); + kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); + } + } + } + + for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { + for (int z = minPoint.getZ(); z < maxPoint.getZ(); z++) { + Set cuboidSet = new HashSet<>(); + for (Cuboid cuboid : cuboids) { + if (y >= cuboid.getY() - 3.5 && y <= cuboid.getDy() + 3.5 && z >= cuboid.getZ() - 3.5 && z <= cuboid.getDz() + 3.5) { + cuboidSet.add(cuboid); + } + } + if (cuboidSet.size() > 1) { + Point p1 = new Point(minPoint.getX(), y, z); + kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); + Point p2 = new Point(maxPoint.getX(), y, z); + kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); + } + } + } + + for (int x = minPoint.getX(); x < maxPoint.getX(); x++) { + for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { + Set cuboidSet = new HashSet<>(); + for (Cuboid cuboid : cuboids) { + if (x >= cuboid.getX() - 3.5 && x <= cuboid.getDx() + 3.5 && y >= cuboid.getY() - 3.5 && y <= cuboid.getDy() + 3.5) { + cuboidSet.add(cuboid); + } + } + if (cuboidSet.size() > 1) { + Point p1 = new Point(x, y, minPoint.getZ()); + kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); + Point p2 = new Point(x, y, maxPoint.getZ()); + kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); + } + } + } + + Set pointSet = new HashSet<>(killCount.keySet()); + for (Point point : pointSet) { + if (!kill.containsKey(point)) { + rEntities.get(point).die(); + rEntities.remove(point); + killCount.remove(point); + } + } + kill.forEach((point, count) -> { + if (rEntities.containsKey(point)) { + if (killCount.get(point) == count) return; + rEntities.get(point).die(); + } + RFallingBlockEntity entity = new RFallingBlockEntity(rEntityServer, point.toLocation(WORLD, 0.5, 0, 0.5), materials[Math.min(count - 1, materials.length) - 1]); + entity.setNoGravity(true); + rEntities.put(point, entity); + killCount.put(point, count); + }); + } + + private Cuboid create(Material type, int x, int y, int z) { + Set checked = new HashSet<>(); + Set points = new HashSet<>(); + points.add(new Point(x, y, z)); + while (!points.isEmpty()) { + Point point = points.iterator().next(); + points.remove(point); + if (!checked.add(point)) continue; + if (point.getX() < minPoint.getX() || point.getX() > maxPoint.getX()) continue; + if (point.getY() < minPoint.getY() || point.getY() > maxPoint.getY()) continue; + if (point.getZ() < minPoint.getZ() || point.getZ() > maxPoint.getZ()) continue; + + if (WORLD.getBlockAt(point.getX() + 1, point.getY(), point.getZ()).getType() == type) { + points.add(new Point(point.getX() + 1, point.getY(), point.getZ())); + } + if (WORLD.getBlockAt(point.getX(), point.getY() + 1, point.getZ()).getType() == type) { + points.add(new Point(point.getX(), point.getY() + 1, point.getZ())); + } + if (WORLD.getBlockAt(point.getX(), point.getY(), point.getZ() + 1).getType() == type) { + points.add(new Point(point.getX(), point.getY(), point.getZ() + 1)); + } + if (WORLD.getBlockAt(point.getX() - 1, point.getY(), point.getZ()).getType() == type) { + points.add(new Point(point.getX() - 1, point.getY(), point.getZ())); + } + if (WORLD.getBlockAt(point.getX(), point.getY() - 1, point.getZ()).getType() == type) { + points.add(new Point(point.getX(), point.getY() - 1, point.getZ())); + } + if (WORLD.getBlockAt(point.getX(), point.getY(), point.getZ() - 1).getType() == type) { + points.add(new Point(point.getX(), point.getY(), point.getZ() - 1)); + } + } + + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + int minZ = Integer.MAX_VALUE; + int maxZ = Integer.MIN_VALUE; + for (Point point : checked) { + if (point.getX() < minX) minX = point.getX(); + if (point.getX() > maxX) maxX = point.getX(); + if (point.getY() < minY) minY = point.getY(); + if (point.getY() > maxY) maxY = point.getY(); + if (point.getZ() < minZ) minZ = point.getZ(); + if (point.getZ() > maxZ) maxZ = point.getZ(); + } + + return new Cuboid(minX, minY, minZ, maxX, maxY, maxZ); + } + + public boolean show(Player player) { + rEntityServer.addPlayer(player); + return players.add(player); + } + + public boolean hide(Player player) { + rEntityServer.removePlayer(player); + players.remove(player); + if (player.isEmpty()) { + rEntityServer.close(); + return true; + } + return false; + } +} diff --git a/build.gradle b/build.gradle index c00c8a83..653fbc20 100644 --- a/build.gradle +++ b/build.gradle @@ -118,12 +118,6 @@ dependencies { } } -task buildResources(type: Copy) { - from("BauSystem_Main/build/classes/java/main/META-INF/annotations/") - into("build/resources/main/de.steamwar.bausystem") -} -classes.finalizedBy(buildResources) - task buildProject { description 'Build this project' group "Steamwar"