Add DynamicRegionVisualizer

This commit is contained in:
2026-03-17 19:14:53 +01:00
parent 5989b0cb04
commit ba2a548f8a
9 changed files with 226 additions and 76 deletions
@@ -20,24 +20,12 @@
package de.steamwar.bausystem.region;
import de.steamwar.bausystem.features.region.RegionCommand;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.region.dynamic.RegionConstructorData;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.core.SWPlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@AbstractSWCommand.PartOf(RegionCommand.class)
public class DynamicRegionCommand extends SWCommand {
@@ -45,7 +33,7 @@ public class DynamicRegionCommand extends SWCommand {
super("");
}
@Register({"dynamic", "visualize"})
@Register({"dynamic"})
public void visualizeRegions(Player player) {
Tile tile = Tile.fromLocation(player.getLocation()).orElse(null);
if (tile == null) return;
@@ -56,53 +44,4 @@ public class DynamicRegionCommand extends SWCommand {
swPlayer.setComponent(new DynamicRegionVisualizer());
}
}
@Register({"dynamic", "place"})
public void placeRegion(Player player, @Mapper("regionType") String regionType) {
Region region = DynamicRegionSystem.INSTANCE.get(player.getLocation());
if (!region.getType().isGlobal()) return;
Tile tile = Tile.fromLocation(player.getLocation()).orElse(null);
if (tile == null) return;
// Check location!
Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.identifierDataMap.get(regionType);
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionClass, UUID.randomUUID(), tile.getMinX(), tile.getMinZ());
if (dynamicRegion == null) {
// TODO: Give error to user
return;
}
dynamicRegion.getArea().place(new PasteBuilder(new PasteBuilder.FileProvider(dynamicRegion.getArea().getResetFile())), false);
dynamicRegion.updateNeighbours();
}
@Register({"dynamic", "delete"})
public void deleteRegion(Player player) {
Region region = DynamicRegionSystem.INSTANCE.get(player.getLocation());
if (!region.getType().isDeletable()) return;
region.delete();
}
@Mapper("regionType")
private TypeMapper<String> regionType() {
return new TypeMapper<>() {
@Override
public String map(CommandSender commandSender, String[] previousArguments, String s) {
for (Map.Entry<Class<? extends DynamicRegion>, RegionConstructorData> entry : DynamicRegionSystem.constructorDataMap.entrySet()) {
if (entry.getValue().name().equalsIgnoreCase(s)) {
return s;
}
}
return null;
}
@Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
return DynamicRegionSystem.constructorDataMap.values()
.stream().map(RegionConstructorData::name)
.collect(Collectors.toSet());
}
};
}
}
@@ -100,6 +100,19 @@ public class DynamicRegionSystem implements RegionSystem {
return GlobalRegion.INSTANCE;
}
public Set<Tile> getTilesOfRegion(@NonNull Region region) {
Point minPoint = region.getArea().getMinPoint(false);
Point maxPoint = region.getArea().getMaxPoint(false);
Set<Tile> tiles = new HashSet<>();
for (int x = minPoint.getX(); x < maxPoint.getX(); x += Tile.tileSize) {
for (int z = minPoint.getZ(); z < maxPoint.getZ(); z += Tile.tileSize) {
tiles.add(Tile.fromXZ(x, z).orElseThrow());
}
}
return Collections.unmodifiableSet(tiles);
}
public @NonNull Region get(@Nullable Tile tile) {
if (tile == null) return getGlobalRegion();
return get(tile.getCenterLocation().getBlockX(), tile.getCenterLocation().getBlockZ(), true, regionMap.values());
@@ -20,20 +20,25 @@
package de.steamwar.bausystem.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.region.dynamic.RegionConstructorData;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.core.SWPlayer;
import de.steamwar.entity.*;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.inventory.SWListInv;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerMoveEvent;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
@@ -43,6 +48,8 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
private Location sourceLocation;
private Tile sourceTile;
private Placement placement = null;
public DynamicRegionVisualizer() {
this.entityServer = new REntityServer();
}
@@ -101,6 +108,21 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
}
}
private void resetTiles(Set<Tile> tiles) {
entityServer.getEntitiesByType(CTile.class)
.stream()
.filter(cTile -> tiles.contains(cTile.tile))
.peek(CTile::die)
.map(cTile -> cTile.tile)
.forEach(tile -> {
new CTile(entityServer, tile);
});
if (placement != null) {
placement.check();
}
}
private class CTile extends CEntity {
private final Tile tile;
@@ -109,7 +131,8 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
super(server);
this.tile = tile;
Material material = switch (DynamicRegionSystem.INSTANCE.get(tile).getType()) {
RegionType regionType = DynamicRegionSystem.INSTANCE.get(tile).getType();
Material material = switch (regionType) {
case SPAWN, SPAWN_EXTENSION -> Material.LODESTONE;
case SPAWN_PATH, PATH -> Material.DIRT_PATH;
case DRY, DRY_SPECIAL -> Material.IRON_BLOCK;
@@ -124,23 +147,176 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
spawn.setShadowed(false);
entities.add(spawn);
} else if (tile.equals(sourceTile)) {
CCubedTextDisplay spawn = new CCubedTextDisplay(entityServer, location);
spawn.setText("§eORIGIN");
spawn.setBackgroundColor(0);
spawn.setShadowed(false);
entities.add(spawn);
CCubedTextDisplay origin = new CCubedTextDisplay(entityServer, location);
origin.setText("§eORIGIN");
origin.setBackgroundColor(0);
origin.setShadowed(false);
entities.add(origin);
}
RBlockDisplay entity = new RBlockDisplay(entityServer, location);
entity.setBlock(material.createBlockData());
entities.add(entity);
RBlockDisplay blockDisplay = new RBlockDisplay(entityServer, location);
blockDisplay.setBlock(material.createBlockData());
entities.add(blockDisplay);
RInteraction interaction = new RInteraction(entityServer, location.clone().add(0.5, 0, 0.5));
interaction.setInteraction((player, entityAction) -> {
SWInventory swInv = new SWInventory(player, () -> Bukkit.createInventory(null, InventoryType.DROPPER, tile.toString()));
swInv.open();
if (placement != null) {
if (regionType.isGlobal()) placement.click(tile);
return;
}
if (!regionType.isGlobal()) {
SWInventory inv = new SWInventory(player, 9, "Delete Region: " + tile.display());
inv.setItem(0, new SWItem(SWItem.getDye(10), "§8Cancel", click -> {
player.closeInventory();
}));
inv.setItem(8, new SWItem(SWItem.getDye(1), "§cDelete", click -> {
player.closeInventory();
Region region = DynamicRegionSystem.INSTANCE.get(tile);
Set<Tile> tiles = DynamicRegionSystem.INSTANCE.getTilesOfRegion(region);
region.delete();
SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class)
.forEach(pair -> {
pair.getComponent().resetTiles(tiles);
});
entities.add(entity);
}));
inv.open();
} else {
List<SWListInv.SWListEntry<Map.Entry<Class<? extends DynamicRegion>, RegionConstructorData>>> entries = new ArrayList<>();
DynamicRegionSystem.constructorDataMap.entrySet()
.stream()
.filter(entry -> entry.getValue().placeable())
.sorted(Comparator.comparing(entry -> entry.getValue().name()))
.forEach(entry -> {
entries.add(new SWListInv.SWListEntry<>(new SWItem(entry.getValue().material(), entry.getValue().name()), entry));
});
SWListInv<Map.Entry<Class<? extends DynamicRegion>, RegionConstructorData>> listInv = new SWListInv<>(player, "Select Region for: " + tile.display(), entries, (click, entry) -> {
new Placement(entry.getKey(), entry.getValue(), tile);
player.closeInventory();
});
listInv.open();
}
});
entities.add(interaction);
}
}
private class Placement {
private final Class<? extends DynamicRegion> regionType;
private final RegionConstructorData constructorData;
private CWireframe wireframe;
private Tile sourceTile;
private int dx;
private int dz;
private boolean valid = false;
private Location getMinLocation() {
Tile tile = sourceTile.add(-DynamicRegionVisualizer.this.sourceTile.getTileX(), -DynamicRegionVisualizer.this.sourceTile.getTileZ()).orElseThrow();
return sourceLocation.clone().add(tile.getTileX(), 0, tile.getTileZ());
}
private Location getMaxLocation() {
Tile tile = sourceTile.add(-DynamicRegionVisualizer.this.sourceTile.getTileX(), -DynamicRegionVisualizer.this.sourceTile.getTileZ()).orElseThrow();
return sourceLocation.clone().add(tile.getTileX(), 0, tile.getTileZ()).add(dx, 0, dz);
}
public Placement(Class<? extends DynamicRegion> regionType, RegionConstructorData constructorData, Tile sourceTile) {
this.regionType = regionType;
this.constructorData = constructorData;
this.sourceTile = sourceTile;
dx = constructorData.widthX() / Tile.tileSize - 1;
dz = constructorData.widthZ() / Tile.tileSize - 1;
if (dx == 0 && dz == 0) {
place();
return;
}
placement = this;
wireframe = new CWireframe(entityServer);
check();
}
private void check() {
wireframe.setPos1And2(getMinLocation(), getMaxLocation());
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
if (!DynamicRegionSystem.INSTANCE.get(sourceTile.add(x, z).orElseThrow()).getType().isGlobal()) {
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
valid = false;
return;
}
}
}
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
valid = true;
}
public void click(Tile tile) {
if (sourceTile.getTileX() >= tile.getTileX() && sourceTile.getTileX() + dx <= tile.getTileX()) {
if (sourceTile.getTileZ() >= tile.getTileZ() && sourceTile.getTileZ() + dz <= tile.getTileZ()) {
if (valid) {
SWInventory inv = new SWInventory(player, 9, "Place Region: " + constructorData.name());
inv.setItem(0, new SWItem(SWItem.getDye(1), "§cDeselect", click -> {
placement = null;
wireframe.die();
player.closeInventory();
}));
inv.setItem(8, new SWItem(SWItem.getDye(10), "§aPlace", click -> {
player.closeInventory();
place();
}));
inv.open();
}
return;
}
}
if (dx == 0 && dz == 0) {
sourceTile = tile;
} else {
Set<Tile> tiles = new HashSet<>();
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
tiles.add(Tile.fromTile(x + sourceTile.getTileX(), z + sourceTile.getTileZ()).orElseThrow());
}
}
Tile selected = tiles.stream().min(Comparator.comparing(current -> {
int dx = current.getTileX() - tile.getTileX();
int dz = current.getTileZ() - tile.getTileZ();
return dx * dx + dz * dz;
}))
.orElseThrow();
int dx = tile.getTileX() - selected.getTileX();
int dz = tile.getTileZ() - selected.getTileZ();
sourceTile = sourceTile.add(dx, dz).orElseThrow();
}
check();
}
private void place() {
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionType, UUID.randomUUID(), sourceTile.getMinX(), sourceTile.getMinZ());
if (dynamicRegion == null) {
// TODO: Give error to user
return;
}
dynamicRegion.getArea().place(new PasteBuilder(new PasteBuilder.FileProvider(dynamicRegion.getArea().getResetFile())), false);
dynamicRegion.updateNeighbours();
placement = null;
if (wireframe != null) wireframe.die();
Set<Tile> tiles = new HashSet<>();
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
tiles.add(Tile.fromTile(x + sourceTile.getTileX(), z + sourceTile.getTileZ()).orElseThrow());
}
}
SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class)
.forEach(pair -> pair.getComponent().resetTiles(tiles));
}
}
}
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.region.dynamic;
import org.atteo.classindex.IndexAnnotated;
import org.bukkit.Material;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -32,6 +33,7 @@ import java.lang.annotation.Target;
public @interface RegionConstructorData {
String identifier();
String name();
Material material();
int widthX();
int widthZ();
boolean placeable() default true;
@@ -138,4 +138,20 @@ public class Tile {
public String toString() {
return tileX + ":" + tileZ;
}
public String display() {
StringBuilder st = new StringBuilder();
if (tileX >= 0) {
st.append("+");
}
st.append(tileX);
st.append(" / ");
if (tileZ >= 0) {
st.append("+");
}
st.append(tileZ);
return st.toString();
}
}
@@ -38,6 +38,7 @@ import java.util.UUID;
@RegionConstructorData(
identifier = "microwargear_display",
name = "MicoWarGearDisplay",
material = Material.STONE_BUTTON,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
@@ -31,6 +31,7 @@ import java.util.UUID;
@RegionConstructorData(
identifier = "path",
name = "Path",
material = Material.DIRT_PATH,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
@@ -38,6 +38,7 @@ import static de.steamwar.bausystem.region.dynamic.special.SpecialArea.SPECIAL_P
@RegionConstructorData(
identifier = "special_dry",
name = "Dry",
material = Material.SAND,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
@@ -37,6 +37,7 @@ import static de.steamwar.bausystem.region.dynamic.special.SpecialArea.SPECIAL_P
@RegionConstructorData(
identifier = "special_wet",
name = "Wet",
material = Material.LIGHT_BLUE_CONCRETE_POWDER,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)