diff --git a/BauSystem_Main/src/BauSystem.properties b/BauSystem_Main/src/BauSystem.properties index 8d03d0ca..695fe723 100644 --- a/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem_Main/src/BauSystem.properties @@ -655,8 +655,10 @@ INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated 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_INFO2 = §7Only colorable blocks like Wool, Terractotta, Stained Glass and Concrete are counted. KILLCHECKER_ENABLE = §aKillchecker activated KILLCHECKER_DISABLE = §cKillchecker deactivated +KILLCHECKER_BOSSBAR = §e§l{0} §7(§e{1}%§7) §e§l{2}§7 cannons # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Toggle on/off diff --git a/BauSystem_Main/src/BauSystem_de.properties b/BauSystem_Main/src/BauSystem_de.properties index f49f1fc6..6dd8044b 100644 --- a/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem_Main/src/BauSystem_de.properties @@ -628,8 +628,10 @@ INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated 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_INFO2 = §7Nur farbige Blöcke wie Wolle, Terracotta, Stained Glass und Concrete wird gezählt. KILLCHECKER_ENABLE = §aKillchecker aktiviert KILLCHECKER_DISABLE = §cKillchecker deaktiviert +KILLCHECKER_BOSSBAR = §e§l{0} §7(§e{1}%§7) §e§l{2}§7 Kanonnen # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Wechsel zwischen an und aus diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java new file mode 100644 index 00000000..08632a9c --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java @@ -0,0 +1,34 @@ +/* + * 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 lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Cuboid { + private double x; + private double y; + private double z; + private double dx; + private double dy; + private double dz; +} \ No newline at end of file diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java index fee15416..1340f1c1 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java @@ -21,8 +21,10 @@ package de.steamwar.bausystem.features.killchecker; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.utils.bossbar.BossBarService; import de.steamwar.command.SWCommand; import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -42,17 +44,21 @@ public class KillcheckerCommand extends SWCommand implements Listener { private Map visualizers = new HashMap<>(); + @LinkedInstance + public BossBarService bossBarService; + public KillcheckerCommand() { super("killchecker"); addDefaultHelpMessage("KILLCHECKER_INFO"); + addDefaultHelpMessage("KILLCHECKER_INFO2"); } @Register(value = "enable", description = "KILLCHECKER_HELP_ENABLE") - public void genericCommand(Player player) { + public void genericCommand(Player player, @OptionalValue("-outline") @StaticValue(value = {"-area", "-outline"}, allowISE = true) boolean onlyOutline) { Region region = Region.getRegion(player.getLocation()); - KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, KillcheckerVisualizer::new); + KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, region1 -> new KillcheckerVisualizer(region1, bossBarService)); killcheckerVisualizer.recalc(); - killcheckerVisualizer.show(player); + killcheckerVisualizer.show(player, onlyOutline); BauSystem.MESSAGE.send("KILLCHECKER_ENABLE", player); } @@ -73,7 +79,7 @@ public class KillcheckerCommand extends SWCommand implements Listener { Player player = event.getPlayer(); Set regions = new HashSet<>(); visualizers.forEach((region, visualizer) -> { - if (visualizer.hide(player)) { + if (visualizer.disconnect(player)) { regions.add(region); } }); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java index e97f4a30..ff21d13b 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java @@ -19,11 +19,13 @@ package de.steamwar.bausystem.features.killchecker; -import de.steamwar.bausystem.features.slaves.laufbau.Cuboid; +import de.steamwar.bausystem.BauSystem; 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.bausystem.utils.bossbar.BauSystemBossbar; +import de.steamwar.bausystem.utils.bossbar.BossBarService; import de.steamwar.entity.REntity; import de.steamwar.entity.REntityServer; import de.steamwar.entity.RFallingBlockEntity; @@ -31,6 +33,9 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; import org.bukkit.entity.Player; import java.util.HashMap; @@ -40,33 +45,59 @@ import java.util.Set; 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 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 static final double SURROUND = 4; + + private final Point minPoint; + private final Point maxPoint; - private Set players = new HashSet<>(); + private final int yArea; + private final int zArea; + private final int xArea; - public KillcheckerVisualizer(Region region) { + private final Set players = new HashSet<>(); + private final Set areaPlayers = new HashSet<>(); + + private final Region region; + private final BossBarService bossBarService; + + public KillcheckerVisualizer(Region region, BossBarService bossBarService) { + this.region = region; this.minPoint = region.getMinPoint(RegionType.BUILD, RegionExtensionType.NORMAL); this.maxPoint = region.getMaxPoint(RegionType.BUILD, RegionExtensionType.NORMAL); + + yArea = (maxPoint.getX() - minPoint.getX()) * (maxPoint.getZ() - minPoint.getZ()); + zArea = (maxPoint.getX() - minPoint.getX()) * (maxPoint.getY() - minPoint.getY()); + xArea = (maxPoint.getY() - minPoint.getY()) * (maxPoint.getZ() - minPoint.getZ()); + + this.bossBarService = bossBarService; } - private REntityServer rEntityServer = new REntityServer(); + private final REntityServer outline = new REntityServer(); + private final REntityServer inner = new REntityServer(); - private Map killCount = new HashMap<>(); - private Map rEntities = new HashMap<>(); + private final Map killCount = new HashMap<>(); + private final Set outlinePointsCache = new HashSet<>(); + private final Map rEntities = new HashMap<>(); + + private double percent = 0; + private int kills = 0; + private int cannonCount = 0; public void recalc() { Set cuboids = new HashSet<>(); Set points = new HashSet<>(); - for (int x = minPoint.getX() + 1; x < maxPoint.getX() - 1; x++) { + for (int x = minPoint.getX() + 1; x < maxPoint.getX(); x++) { for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { - for (int z = minPoint.getZ() + 1; z < maxPoint.getZ() - 1; z++) { + for (int z = minPoint.getZ() + 1; z < maxPoint.getZ(); z++) { if (points.contains(new Point(x, y, z))) continue; Block block = WORLD.getBlockAt(x, y, z); if (block.getType().isAir()) continue; + String name = block.getType().name(); + if (!name.endsWith("_WOOL") && !name.endsWith("_STAINED_GLASS") && !name.endsWith("_CONCRETE") && !name.endsWith("_TERRACOTTA")) continue; + if (name.equals("_GLAZED_TERRACOTTA")) continue; Cuboid cuboid = create(block.getType(), x, y, z); cuboids.add(cuboid); for (int dx = (int) cuboid.getX(); dx <= cuboid.getDx(); dx++) { @@ -79,60 +110,162 @@ public class KillcheckerVisualizer { } } } + cannonCount = cuboids.size(); Map kill = new HashMap<>(); - for (int x = minPoint.getX(); x < maxPoint.getX(); x++) { - for (int z = minPoint.getZ(); z < maxPoint.getZ(); z++) { + int yKills = 0; + int yCount = 0; + Set yPoints = new HashSet<>(); + 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) { + if (x >= cuboid.getX() - SURROUND && x < cuboid.getDx() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) { 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); + yCount++; + yKills += splitIntoDoubleKills(cuboidSet.size()); + Point p2 = new Point(x, maxPoint.getY() + 1, z); + yPoints.add(p2); 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++) { + int xKills = 0; + int xCount = 0; + Set xPoints = new HashSet<>(); + 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) { + if (y >= cuboid.getY() - SURROUND && y < cuboid.getDy() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) { cuboidSet.add(cuboid); } } if (cuboidSet.size() > 1) { - Point p1 = new Point(minPoint.getX(), y, z); + xCount++; + xKills += splitIntoDoubleKills(cuboidSet.size()); + Point p1 = new Point(minPoint.getX() - 1, y, z); + xPoints.add(p1); kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); - Point p2 = new Point(maxPoint.getX(), y, z); + Point p2 = new Point(maxPoint.getX() + 1, y, z); + xPoints.add(p2); 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++) { + int zKills = 0; + int zCount = 0; + Set zPoints = new HashSet<>(); + 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) { + if (x >= cuboid.getX() - SURROUND && x < cuboid.getDx() + SURROUND && y >= cuboid.getY() - SURROUND && y < cuboid.getDy() + SURROUND) { cuboidSet.add(cuboid); } } if (cuboidSet.size() > 1) { - Point p1 = new Point(x, y, minPoint.getZ()); + zCount++; + zKills += splitIntoDoubleKills(cuboidSet.size()); + Point p1 = new Point(x, y, minPoint.getZ() - 1); + zPoints.add(p1); kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); - Point p2 = new Point(x, y, maxPoint.getZ()); + Point p2 = new Point(x, y, maxPoint.getZ() + 1); + zPoints.add(p2); kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); } } } + Set outlinePoints = new HashSet<>(); + yPoints.forEach(point -> { + Point p1 = new Point(point.getX() - 1, point.getY(), point.getZ()); + Point p2 = new Point(point.getX() + 1, point.getY(), point.getZ()); + Point p3 = new Point(point.getX(), point.getY(), point.getZ() - 1); + Point p4 = new Point(point.getX(), point.getY(), point.getZ() + 1); + + Point p5 = new Point(point.getX() - 1, point.getY(), point.getZ() - 1); + Point p6 = new Point(point.getX() - 1, point.getY(), point.getZ() + 1); + Point p7 = new Point(point.getX() + 1, point.getY(), point.getZ() - 1); + Point p8 = new Point(point.getX() + 1, point.getY(), point.getZ() + 1); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + xPoints.forEach(point -> { + Point p1 = new Point(point.getX(), point.getY() - 1, point.getZ()); + Point p2 = new Point(point.getX(), point.getY() + 1, point.getZ()); + Point p3 = new Point(point.getX(), point.getY(), point.getZ() - 1); + Point p4 = new Point(point.getX(), point.getY(), point.getZ() + 1); + + Point p5 = new Point(point.getX(), point.getY() - 1, point.getZ() - 1); + Point p6 = new Point(point.getX(), point.getY() - 1, point.getZ() + 1); + Point p7 = new Point(point.getX(), point.getY() + 1, point.getZ() - 1); + Point p8 = new Point(point.getX(), point.getY() + 1, point.getZ() + 1); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + zPoints.forEach(point -> { + Point p1 = new Point(point.getX() - 1, point.getY(), point.getZ()); + Point p2 = new Point(point.getX() + 1, point.getY(), point.getZ()); + Point p3 = new Point(point.getX(), point.getY() - 1, point.getZ()); + Point p4 = new Point(point.getX(), point.getY() + 1, point.getZ()); + + Point p5 = new Point(point.getX() - 1, point.getY() - 1, point.getZ()); + Point p6 = new Point(point.getX() - 1, point.getY() + 1, point.getZ()); + Point p7 = new Point(point.getX() + 1, point.getY() - 1, point.getZ()); + Point p8 = new Point(point.getX() + 1, point.getY() + 1, point.getZ()); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + + double xPercent = zCount / (double) xArea; + double yPercent = yCount / (double) yArea; + double zPercent = xCount / (double) zArea; + percent = (xPercent + yPercent + zPercent) / 3; + kills = zKills + yKills + xKills; + players.forEach(this::updateBossBar); + Set pointSet = new HashSet<>(killCount.keySet()); + Set outlinePointsCacheLast = new HashSet<>(outlinePointsCache); + outlinePointsCache.clear(); for (Point point : pointSet) { if (!kill.containsKey(point)) { rEntities.get(point).die(); @@ -142,16 +275,37 @@ public class KillcheckerVisualizer { } kill.forEach((point, count) -> { if (rEntities.containsKey(point)) { - if (killCount.get(point) == count) return; + if (killCount.get(point) == count && outlinePoints.contains(point) == outlinePointsCacheLast.contains(point)) 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]); + RFallingBlockEntity entity = new RFallingBlockEntity(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5), MATERIALS[Math.min(count - 1, MATERIALS.length) - 1]); entity.setNoGravity(true); rEntities.put(point, entity); + if (outlinePoints.contains(point)) outlinePointsCache.add(point); killCount.put(point, count); }); } + private int splitIntoDoubleKills(int kills) { + return kills * (kills - 1) / 2; + } + + private void updateBossBar(Player player) { + BauSystemBossbar bossbar = bossBarService.get(player, region, "killchecker"); + bossbar.setTitle(BauSystem.MESSAGE.parse("KILLCHECKER_BOSSBAR", player, kills, ((int) (percent * 1000) / 10.0), cannonCount)); + bossbar.setProgress(percent); + + if (percent >= 0.35) { + bossbar.setColor(BarColor.RED); + } else if (percent >= 0.25) { + bossbar.setColor(BarColor.PURPLE); + } else if (percent >= 0.15) { + bossbar.setColor(BarColor.YELLOW); + } else { + bossbar.setColor(BarColor.GREEN); + } + } + private Cuboid create(Material type, int x, int y, int z) { Set checked = new HashSet<>(); Set points = new HashSet<>(); @@ -202,16 +356,42 @@ public class KillcheckerVisualizer { return new Cuboid(minX, minY, minZ, maxX, maxY, maxZ); } - public boolean show(Player player) { - rEntityServer.addPlayer(player); + public boolean show(Player player, boolean onlyOutline) { + outline.addPlayer(player); + if (!onlyOutline) { + inner.addPlayer(player); + areaPlayers.add(player); + } else if (areaPlayers.contains(player)) { + inner.removePlayer(player); + areaPlayers.remove(player); + } + updateBossBar(player); return players.add(player); } public boolean hide(Player player) { - rEntityServer.removePlayer(player); + outline.removePlayer(player); + if (areaPlayers.contains(player)) { + inner.removePlayer(player); + } players.remove(player); + areaPlayers.remove(player); + bossBarService.remove(player, region, "killchecker"); if (players.isEmpty()) { - rEntityServer.close(); + outline.close(); + inner.close(); + return true; + } + return false; + } + + public boolean disconnect(Player player) { + players.remove(player); + areaPlayers.remove(player); + bossBarService.remove(player, region, "killchecker"); + if (players.isEmpty()) { + outline.close(); + inner.close(); return true; } return false; diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java new file mode 100644 index 00000000..e72e0b53 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java @@ -0,0 +1,51 @@ +/* + * 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.utils.bossbar; + +import de.steamwar.bausystem.region.Region; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; + +public interface BauSystemBossbar { + + String getTitle(); + void setTitle(String title); + + double getProgress(); + void setProgress(double progress); + + BarColor getColor(); + void setColor(BarColor color); + + BarStyle getStyle(); + void setStyle(BarStyle style); + + boolean hasFlag(BarFlag flag); + void addFlag(BarFlag flag); + void removeFlag(BarFlag flag); + + boolean isVisible(); + void setVisible(boolean visible); + + Region getRegion(); + + void cleanup(); +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java new file mode 100644 index 00000000..6c7d636d --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java @@ -0,0 +1,90 @@ +/* + * 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.utils.bossbar; + +import de.steamwar.bausystem.region.Region; +import de.steamwar.linkage.Linked; +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.Map; + +@Linked +public class BossBarService implements Listener { + + private final Map>> playerBossBars = new HashMap<>(); + + public synchronized BauSystemBossbar get(Player player, Region region, String key) { + return playerBossBars.computeIfAbsent(player, p -> new HashMap<>()) + .computeIfAbsent(region, r -> new HashMap<>()) + .computeIfAbsent(key, k -> { + BossBar bossBar = Bukkit.createBossBar("", BarColor.WHITE, BarStyle.SOLID); + bossBar.addPlayer(player); + if (region.isGlobal()) { + return new GlobalBossbar(bossBar); + } else { + return new RegionedBossbar(bossBar, region, player); + } + }); + } + + public synchronized void removeAll(Player player, String key) { + Map> regionMap = playerBossBars.get(player); + if (regionMap == null) return; + for (Map bossBarMap : regionMap.values()) { + BauSystemBossbar bossBar = bossBarMap.remove(key); + if (bossBar == null) continue; + bossBar.cleanup(); + } + } + + public synchronized void removeAll(Player player) { + Map> regionMap = playerBossBars.remove(player); + if (regionMap == null) return; + for (Map bossBarMap : regionMap.values()) { + for (BauSystemBossbar bossBar : bossBarMap.values()) { + bossBar.cleanup(); + } + } + } + + public synchronized void remove(Player player, Region region, String key) { + Map> regionMap = playerBossBars.get(player); + if (regionMap == null) return; + Map bossBarMap = regionMap.get(region); + if (bossBarMap == null) return; + BauSystemBossbar bossBar = bossBarMap.remove(key); + if (bossBar == null) return; + bossBar.cleanup(); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + removeAll(event.getPlayer()); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java new file mode 100644 index 00000000..a5aaf9fe --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java @@ -0,0 +1,112 @@ +/* + * 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.utils.bossbar; + +import de.steamwar.bausystem.region.GlobalRegion; +import de.steamwar.bausystem.region.Region; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; + +public class GlobalBossbar implements BauSystemBossbar { + + private BossBar bossBar; + + public GlobalBossbar(BossBar bossBar) { + this.bossBar = bossBar; + } + + @Override + public String getTitle() { + return bossBar.getTitle(); + } + + @Override + public void setTitle(String title) { + bossBar.setTitle(title); + } + + @Override + public double getProgress() { + return bossBar.getProgress(); + } + + @Override + public void setProgress(double progress) { + bossBar.setProgress(progress); + } + + @Override + public BarColor getColor() { + return bossBar.getColor(); + } + + @Override + public void setColor(BarColor color) { + bossBar.setColor(color); + } + + @Override + public BarStyle getStyle() { + return bossBar.getStyle(); + } + + @Override + public void setStyle(BarStyle style) { + bossBar.setStyle(style); + } + + @Override + public boolean hasFlag(BarFlag flag) { + return bossBar.hasFlag(flag); + } + + @Override + public void addFlag(BarFlag flag) { + bossBar.addFlag(flag); + } + + @Override + public void removeFlag(BarFlag flag) { + bossBar.removeFlag(flag); + } + + @Override + public boolean isVisible() { + return bossBar.isVisible(); + } + + @Override + public void setVisible(boolean visible) { + bossBar.setVisible(visible); + } + + @Override + public Region getRegion() { + return GlobalRegion.getInstance(); + } + + @Override + public void cleanup() { + bossBar.removeAll(); + bossBar = null; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java new file mode 100644 index 00000000..4d922143 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java @@ -0,0 +1,146 @@ +/* + * 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.utils.bossbar; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.region.utils.RegionExtensionType; +import de.steamwar.bausystem.region.utils.RegionType; +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class RegionedBossbar implements BauSystemBossbar, Listener { + + private BossBar bossBar; + private Region region; + private Player player; + + public RegionedBossbar(BossBar bossBar, Region region, Player player) { + this.bossBar = bossBar; + this.region = region; + this.player = player; + Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance()); + } + + @Override + public String getTitle() { + return bossBar.getTitle(); + } + + @Override + public void setTitle(String title) { + bossBar.setTitle(title); + } + + @Override + public double getProgress() { + return bossBar.getProgress(); + } + + @Override + public void setProgress(double progress) { + bossBar.setProgress(progress); + } + + @Override + public BarColor getColor() { + return bossBar.getColor(); + } + + @Override + public void setColor(BarColor color) { + bossBar.setColor(color); + } + + @Override + public BarStyle getStyle() { + return bossBar.getStyle(); + } + + @Override + public void setStyle(BarStyle style) { + bossBar.setStyle(style); + } + + @Override + public boolean hasFlag(BarFlag flag) { + return bossBar.hasFlag(flag); + } + + @Override + public void addFlag(BarFlag flag) { + bossBar.addFlag(flag); + } + + @Override + public void removeFlag(BarFlag flag) { + bossBar.removeFlag(flag); + } + + @Override + public boolean isVisible() { + return bossBar.isVisible(); + } + + @Override + public void setVisible(boolean visible) { + bossBar.setVisible(visible); + } + + @Override + public Region getRegion() { + return region; + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if (event.getPlayer() != player) return; + if (region.inRegion(event.getTo(), RegionType.NORMAL, RegionExtensionType.NORMAL)) { + bossBar.addPlayer(player); + } else { + bossBar.removePlayer(player); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + if (event.getPlayer() != player) return; + cleanup(); + } + + @Override + public void cleanup() { + bossBar.removeAll(); + bossBar = null; + region = null; + player = null; + + PlayerMoveEvent.getHandlerList().unregister(this); + PlayerQuitEvent.getHandlerList().unregister(this); + } +}