fix: позиция голограмм (getBlock.Location) + зона ворот (corner1/corner2)

This commit is contained in:
loki5512344 2026-06-14 17:49:28 +02:00
parent b0afb3f6be
commit eb4a670e9c
8 changed files with 124 additions and 13 deletions

View file

@ -73,7 +73,7 @@ public class ChestHologram {
continue; continue;
} }
Location holoLoc = loc.clone().add(0.5, 2.0 + heightOffset, 0.5); Location holoLoc = loc.getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
try { try {
if (useTextDisplay) { if (useTextDisplay) {
TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> { TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> {
@ -139,11 +139,11 @@ public class ChestHologram {
if (display instanceof TextDisplay td) { if (display instanceof TextDisplay td) {
td.text(TextUtil.color(text)); td.text(TextUtil.color(text));
Location base = chest.getLocation().clone().add(0.5, 2.0 + heightOffset, 0.5); Location base = chest.getLocation().getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
td.teleport(base); td.teleport(base);
} else if (display instanceof ArmorStand as) { } else if (display instanceof ArmorStand as) {
as.setCustomName(text); as.setCustomName(text);
Location base = chest.getLocation().clone().add(0.5, 2.0 + heightOffset, 0.5); Location base = chest.getLocation().getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
as.teleport(base); as.teleport(base);
} }
} }

View file

@ -201,6 +201,7 @@ public class SetupCommand implements TabExecutor {
var locations = plugin.getConfigManager().getLocations(); var locations = plugin.getConfigManager().getLocations();
locations.setGate(gateId, center); locations.setGate(gateId, center);
locations.setGateZone(gateId, p1, p2);
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig()); plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
player.sendMessage(messages().get("setup.gate-placed", gateId, formatLoc(center))); player.sendMessage(messages().get("setup.gate-placed", gateId, formatLoc(center)));

View file

@ -225,12 +225,24 @@ public class LocationsConfig {
config.set(path, list); config.set(path, list);
} }
/** Устанавливает ворота A или B (хранятся как gate-a / gate-b). */ /** Устанавливает ворота A или B с зоной (хранятся как gate-a / gate-b + corner1/corner2). */
public void setGate(String id, Location loc) { public void setGate(String id, Location loc) {
String key = "gate-" + id.toLowerCase(); String key = "gate-" + id.toLowerCase();
config.set(key, locationToString(loc)); config.set(key, locationToString(loc));
} }
public void setGateZone(String id, Location corner1, Location corner2) {
String prefix = "gate-" + id.toLowerCase();
config.set(prefix + "-corner1", locationToString(corner1));
config.set(prefix + "-corner2", locationToString(corner2));
}
public Location getGateCorner(String id, int num) {
String key = "gate-" + id.toLowerCase() + "-corner" + num;
String str = config.getString(key);
return str != null ? parseLocation(str) : null;
}
public Location getGate(String id) { public Location getGate(String id) {
String key = "gate-" + id.toLowerCase(); String key = "gate-" + id.toLowerCase();
String str = config.getString(key); String str = config.getString(key);
@ -251,6 +263,15 @@ public class LocationsConfig {
return gates; return gates;
} }
/** Get all gate IDs that exist in config. */
public List<String> getGateIds() {
List<String> ids = new ArrayList<>();
for (String id : List.of("a", "b")) {
if (getGate(id) != null) ids.add(id);
}
return ids;
}
private String locationToString(Location loc) { private String locationToString(Location loc) {
// Use | as separator instead of , to avoid issues with negative coordinates in YAML // Use | as separator instead of , to avoid issues with negative coordinates in YAML
return String.format(Locale.US, "%s|%.2f|%.2f|%.2f|%.1f|%.1f", return String.format(Locale.US, "%s|%.2f|%.2f|%.2f|%.1f|%.1f",

View file

@ -11,6 +11,10 @@ public class ExitGate {
private double savedProgress; // сохраняется при уходе игрока private double savedProgress; // сохраняется при уходе игрока
private boolean powered; private boolean powered;
private int zoneMinX, zoneMinY, zoneMinZ;
private int zoneMaxX, zoneMaxY, zoneMaxZ;
private boolean hasZone;
public enum GateState { public enum GateState {
CLOSED, CLOSED,
OPENING, OPENING,
@ -23,6 +27,22 @@ public class ExitGate {
this.progress = 0.0; this.progress = 0.0;
this.savedProgress = 0.0; this.savedProgress = 0.0;
this.powered = false; this.powered = false;
this.hasZone = false;
}
/** Set the gate zone from two corner locations. */
public void setZone(Location corner1, Location corner2) {
zoneMinX = Math.min(corner1.getBlockX(), corner2.getBlockX());
zoneMinY = Math.min(corner1.getBlockY(), corner2.getBlockY());
zoneMinZ = Math.min(corner1.getBlockZ(), corner2.getBlockZ());
zoneMaxX = Math.max(corner1.getBlockX(), corner2.getBlockX());
zoneMaxY = Math.max(corner1.getBlockY(), corner2.getBlockY());
zoneMaxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ());
hasZone = true;
}
private boolean hasZone() {
return hasZone;
} }
public Location getLocation() { public Location getLocation() {
@ -35,12 +55,71 @@ public class ExitGate {
location.getBlock().setType(org.bukkit.Material.HOPPER, false); location.getBlock().setType(org.bukkit.Material.HOPPER, false);
} }
/** Build the gate structure fills the entire designated zone. */
public void spawnGateStructure() {
World world = location.getWorld();
if (world == null) return;
if (hasZone) {
for (int x = zoneMinX; x <= zoneMaxX; x++) {
for (int y = zoneMinY; y <= zoneMaxY; y++) {
for (int z = zoneMinZ; z <= zoneMaxZ; z++) {
world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, false);
}
}
}
} else {
// Fallback: 3x3x3 zone around gate location
int cx = location.getBlockX(), cy = location.getBlockY(), cz = location.getBlockZ();
for (int x = cx - 1; x <= cx + 1; x++) {
for (int y = cy; y <= cy + 2; y++) {
for (int z = cz - 1; z <= cz + 1; z++) {
world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, false);
}
}
}
}
// Button at gate center + (1, 1, 1)
world.getBlockAt(location.clone().add(1, 1, 1)).setType(org.bukkit.Material.STONE_BUTTON, false);
}
/** Remove the interaction block (e.g. when gate opens or on cleanup). */ /** Remove the interaction block (e.g. when gate opens or on cleanup). */
public void removeGateBlock() { public void removeGateBlock() {
if (location.getWorld() == null) return; if (location.getWorld() == null) return;
location.getBlock().setType(org.bukkit.Material.AIR, false); location.getBlock().setType(org.bukkit.Material.AIR, false);
} }
/** Remove the entire gate zone + interaction block. */
public void removeGateStructure() {
World world = location.getWorld();
if (world == null) return;
if (hasZone) {
for (int x = zoneMinX; x <= zoneMaxX; x++) {
for (int y = zoneMinY; y <= zoneMaxY; y++) {
for (int z = zoneMinZ; z <= zoneMaxZ; z++) {
world.getBlockAt(x, y, z).setType(org.bukkit.Material.AIR, false);
}
}
}
} else {
int cx = location.getBlockX(), cy = location.getBlockY(), cz = location.getBlockZ();
for (int x = cx - 1; x <= cx + 1; x++) {
for (int y = cy; y <= cy + 2; y++) {
for (int z = cz - 1; z <= cz + 1; z++) {
world.getBlockAt(x, y, z).setType(org.bukkit.Material.AIR, false);
}
}
}
}
// Remove button at (1, 1, 1)
world.getBlockAt(location.clone().add(1, 1, 1)).setType(org.bukkit.Material.AIR, false);
removeGateBlock();
}
public GateState getState() { public GateState getState() {
return state; return state;
} }
@ -102,7 +181,8 @@ public class ExitGate {
if (progress >= 1.0) { if (progress >= 1.0) {
progress = 1.0; progress = 1.0;
state = GateState.OPEN; state = GateState.OPEN;
removeGateBlock(); removeGateStructure();
spawnOpenParticles();
playOpenedSound(); playOpenedSound();
} }
} }

View file

@ -19,9 +19,17 @@ public class GateManager {
} }
private void loadGates(LocationsConfig locations) { private void loadGates(LocationsConfig locations) {
List<Location> gateLocations = locations.getExitGates(); for (String id : locations.getGateIds()) {
for (int i = 0; i < Math.min(gateLocations.size(), GATE_COUNT); i++) { Location center = locations.getGate(id);
gates.add(new ExitGate(gateLocations.get(i))); if (center == null) continue;
ExitGate gate = new ExitGate(center);
Location c1 = locations.getGateCorner(id, 1);
Location c2 = locations.getGateCorner(id, 2);
if (c1 != null && c2 != null) {
gate.setZone(c1, c2);
}
gates.add(gate);
if (gates.size() >= GATE_COUNT) break;
} }
} }

View file

@ -72,7 +72,7 @@ public class HookHologram {
continue; continue;
} }
Location holoLoc = loc.clone().add(0.5, 2.0 + heightOffset, 0.5); Location holoLoc = loc.getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
try { try {
if (useTextDisplay) { if (useTextDisplay) {
TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> { TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> {
@ -128,7 +128,7 @@ public class HookHologram {
String text = "§c⚓ Крюк §f#" + visibleIndex + "/" + total + "\n§7" + status; String text = "§c⚓ Крюк §f#" + visibleIndex + "/" + total + "\n§7" + status;
double heightOffset = plugin.getConfigManager().getGame().getHookHologramHeightOffset(); double heightOffset = plugin.getConfigManager().getGame().getHookHologramHeightOffset();
Location base = hook.getLocation().clone().add(0.5, 2.0 + heightOffset, 0.5); Location base = hook.getLocation().getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
if (display instanceof TextDisplay td) { if (display instanceof TextDisplay td) {
td.text(TextUtil.color(text)); td.text(TextUtil.color(text));

View file

@ -67,7 +67,7 @@ public class GeneratorHologram {
Location loc = gen.getLocation(); Location loc = gen.getLocation();
if (loc.getWorld() == null) continue; if (loc.getWorld() == null) continue;
Location holoLoc = loc.clone().add(0.5, 2.0 + heightOffset, 0.5); Location holoLoc = loc.getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
try { try {
if (useTextDisplay) { if (useTextDisplay) {
TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> { TextDisplay display = loc.getWorld().spawn(holoLoc, TextDisplay.class, d -> {
@ -118,7 +118,7 @@ public class GeneratorHologram {
text = "§6⚙ Генератор §f#" + (gen.getId() + 1) + "/" + total + "\n§e" + bar + " §f" + pct + "%"; text = "§6⚙ Генератор §f#" + (gen.getId() + 1) + "/" + total + "\n§e" + bar + " §f" + pct + "%";
} }
Location base = gen.getLocation().clone().add(0.5, 2.0 + heightOffset, 0.5); Location base = gen.getLocation().getBlock().getLocation().add(0.5, 2.0 + heightOffset, 0.5);
if (display instanceof TextDisplay td) { if (display instanceof TextDisplay td) {
td.text(TextUtil.color(text)); td.text(TextUtil.color(text));

View file

@ -321,8 +321,9 @@ public class GameplayPhase implements Phase {
} }
plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + hookManager.getAllHooks().size() + " hooks"); plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + hookManager.getAllHooks().size() + " hooks");
// Спавн блоков на местах ворот (Hopper — для открытия через ПКМ) // Спавн структуры ворот (рамка + кнопка + Hopper для взаимодействия)
for (ExitGate gate : gateManager.getAllGates()) { for (ExitGate gate : gateManager.getAllGates()) {
gate.spawnGateStructure();
gate.spawnGateBlock(); gate.spawnGateBlock();
} }
plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + gateManager.getAllGates().size() + " gates"); plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + gateManager.getAllGates().size() + " gates");