diff --git a/src/de/steamwar/lobby/command/PortalCommand.java b/src/de/steamwar/lobby/command/PortalCommand.java index d069d6c..2d892f2 100644 --- a/src/de/steamwar/lobby/command/PortalCommand.java +++ b/src/de/steamwar/lobby/command/PortalCommand.java @@ -83,6 +83,11 @@ public class PortalCommand extends SWCommand { new Portal(portalName, tuple.k, tuple.v, portal -> new StackPortal(portal, portalDestination, String.join(" ", command))); } + @Register({"depth"}) + public void portalDepth(Player player, Portal portal, double depth) { + portal.setDepth(depth); + } + @Register({"addblue"}) public void portalAddBlue(Player player, Portal portal) { FightserverPortal handler = (FightserverPortal) portal.getHandler(); diff --git a/src/de/steamwar/lobby/portal/FightserverPortal.java b/src/de/steamwar/lobby/portal/FightserverPortal.java index 21e65f5..e2546c3 100644 --- a/src/de/steamwar/lobby/portal/FightserverPortal.java +++ b/src/de/steamwar/lobby/portal/FightserverPortal.java @@ -76,7 +76,7 @@ public class FightserverPortal implements PortalHandler, Comparable portals = new HashMap<>(); + private static final Map> chunkPortals = new HashMap<>(); public static List getPortals() { return new ArrayList<>(portals.values()); @@ -42,17 +44,22 @@ public class Portal implements PortalHandler, ConfigurationSerializable { } public static Portal getPortal(Location from, Location to) { - Vector movement = to.toVector().subtract(from.toVector()); + ChunkCoords in = new ChunkCoords(from); + ChunkCoords out = new ChunkCoords(to); + for(ChunkCoords coords : perChunk(Math.min(in.x, out.x), Math.max(in.x, out.x), Math.min(in.z, out.z), Math.max(in.z, out.z))) { + for(Portal portal : chunkPortals.getOrDefault(coords, Collections.emptyList())) { + Vector normalizedFrom = portal.normalize(from); + Vector normalizedTo = portal.normalize(to); - for(Portal portal : portals.values()) { - double d = portal.orientation.getX() * movement.getZ() - portal.orientation.getZ() * movement.getX(); - if(d == 0 || !insidePortal(to.getY(), portal.pos1.getY(), portal.pos2.getY())) - continue; + if(portal.depth == 0.0) { + normalizedFrom.setX(normalizedFrom.getX() > 0 ? 2 : -1); + normalizedTo.setX(normalizedTo.getX() > 0 ? 2 : -1); + } - double t = ((portal.pos2.getX() - to.getX()) * movement.getZ() - (portal.pos2.getZ() - to.getZ()) * movement.getX()) / d; - double u = ((portal.pos2.getX() - to.getX()) * portal.orientation.getZ() - (portal.pos2.getZ() - to.getZ()) * portal.orientation.getX()) / d; - if(0 <= t && t <= 1 && 0 <= u && u <= 1) - return portal; + if(inside(normalizedFrom.getX(), normalizedTo.getX()) && inside(normalizedFrom.getY(), normalizedTo.getY()) && inside(normalizedFrom.getZ(), normalizedTo.getZ())) { + return portal; + } + } } return null; } @@ -61,44 +68,103 @@ public class Portal implements PortalHandler, ConfigurationSerializable { return portals.get(id); } - private static boolean insidePortal(double x, double p1, double p2) { - return x < p1 ^ x < p2; + private static boolean inside(double v1, double v2) { + return (v1 >= 0.0 || v2 >= 0.0) && (v1 <= 1.0 || v2 <= 1.0); } private final Location pos1; private final Location pos2; - private final Vector orientation; + private double depth; + + private final Vector pos1Vector; + private final double yRotation; + private final Vector rotatedShape; + + private final int minChunkX; + private final int minChunkZ; + private final int maxChunkX; + private final int maxChunkZ; private final String id; private final PortalType type; private final PortalHandler handler; + private final Hologram debugPos1; + private final Hologram debugPos2; public Portal(Map map) { - this.id = (String) map.get("id"); - this.pos1 = (Location) map.get("pos1"); - this.pos2 = (Location) map.get("pos2"); - this.orientation = pos2.toVector().subtract(pos1.toVector()); - this.type = PortalType.valueOf((String) map.get("type")); - this.handler = type.deserialize(map, this); - - init(); + this( + (String) map.get("id"), + (Location) map.get("pos1"), + (Location) map.get("pos2"), + (double) map.getOrDefault("depth", 0.0), + portal -> PortalType.valueOf((String) map.get("type")).deserialize(map, portal) + ); } public Portal(String id, Location pos1, Location pos2, Function handlerConstructor) { - this.id = id; - this.pos1 = pos1; - this.pos2 = pos2; - this.orientation = pos2.toVector().subtract(pos1.toVector()); - this.handler = handlerConstructor.apply(this); - this.type = handler.type(); - - init(); + this(id, pos1, pos2, 0.0, handlerConstructor); LobbySystem.config().save(); } - private void init() { + public Portal(String id, Location pos1, Location pos2, double depth, Function handlerConstructor) { + this.id = id; + this.pos1 = pos1; + this.pos2 = pos2; + this.depth = depth; + + this.minChunkX = Math.min(pos1.getBlockX(), pos2.getBlockX()) >> 4; + this.minChunkZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()) >> 4; + this.maxChunkX = Math.max(pos1.getBlockX(), pos2.getBlockX()) >> 4; + this.maxChunkZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ()) >> 4; + + Vector orientation = pos2.toVector().subtract(pos1.toVector()); + this.yRotation = Math.atan2(orientation.getX(), orientation.getZ()); + this.pos1Vector = pos1.toVector().subtract(new Vector(depth/2, 0, 0).rotateAroundY(-yRotation)); + this.rotatedShape = new Vector(depth == 0.0 ? 1.0 : depth, orientation.getY(), orientation.clone().setY(0).length()); + + this.handler = handlerConstructor.apply(this); + this.type = handler.type(); + + this.debugPos1 = new Hologram(null, pos1, id + " POS1"); + this.debugPos2 = new Hologram(null, pos2, id + " POS2"); + portals.put(id, this); + perChunk(minChunkX, maxChunkX, minChunkZ, maxChunkZ).forEach(coords -> chunkPortals.computeIfAbsent(coords, coords1 -> new ArrayList<>()).add(this)); + } + + public Vector normalize(Location location) { + return location.toVector().subtract(pos1Vector).rotateAroundY(-yRotation).divide(rotatedShape); + } + + public Vector denormalize(Vector vector) { + return vector.multiply(rotatedShape).rotateAroundY(yRotation).add(pos1Vector); + } + + public double getYrotation() { + return yRotation; + } + + private static Iterable perChunk(int minChunkX, int maxChunkX, int minChunkZ, int maxChunkZ) { + return () -> new Iterator() { + private int x = minChunkX; + private int z = minChunkZ; + + @Override + public boolean hasNext() { + return z <= maxChunkZ; + } + + @Override + public ChunkCoords next() { + ChunkCoords coords = new ChunkCoords(x++, z); + if (x > maxChunkX) { + x = minChunkX; + z++; + } + return coords; + } + }; } @Override @@ -112,6 +178,7 @@ public class Portal implements PortalHandler, ConfigurationSerializable { map.put("id", id); map.put("pos1", pos1); map.put("pos2", pos2); + map.put("depth", depth); map.put("type", type.name()); handler.serialize(map); } @@ -123,7 +190,10 @@ public class Portal implements PortalHandler, ConfigurationSerializable { @Override public void delete() { + debugPos1.delete(); + debugPos2.delete(); handler.delete(); + perChunk(minChunkX, maxChunkX, minChunkZ, maxChunkZ).forEach(coords -> chunkPortals.getOrDefault(coords, new ArrayList<>()).remove(this)); portals.remove(id); LobbySystem.config().save(); } @@ -143,14 +213,18 @@ public class Portal implements PortalHandler, ConfigurationSerializable { return pos2; } - public Vector getOrientation() { - return orientation; - } - public String getId() { return id; } + public void setDepth(double depth) { + delete(); + Map handlerMap = new HashMap<>(); + handler.serialize(handlerMap); + new Portal(id, pos1, pos2, depth, portal -> handler.type().deserialize(handlerMap, portal)); + LobbySystem.config().save(); + } + public PortalHandler getHandler() { return handler; } @@ -159,4 +233,34 @@ public class Portal implements PortalHandler, ConfigurationSerializable { public String toString() { return getId() + " " + type().name(); } + + private static class ChunkCoords { + + private final int x; + private final int z; + + private ChunkCoords(Location location) { + this.x = location.getBlockX() >> 4; + this.z = location.getBlockZ() >> 4; + } + + private ChunkCoords(int x, int z) { + this.x = x; + this.z = z; + } + + @Override + public int hashCode() { + return x << 16 + z; + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof ChunkCoords)) + return false; + + ChunkCoords coords = (ChunkCoords) obj; + return x == coords.x && z == coords.z; + } + } } diff --git a/src/de/steamwar/lobby/portal/TeleportPortal.java b/src/de/steamwar/lobby/portal/TeleportPortal.java index 2f52ae6..fd0fe9c 100644 --- a/src/de/steamwar/lobby/portal/TeleportPortal.java +++ b/src/de/steamwar/lobby/portal/TeleportPortal.java @@ -23,7 +23,6 @@ import de.steamwar.lobby.listener.Portals; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.util.Vector; import java.util.*; @@ -58,37 +57,20 @@ public class TeleportPortal implements PortalHandler { public void handle(Player player, Location from, Location to) { Deque stack = Portals.getStack(player); if(!stack.isEmpty() && sources.contains(stack.peek().getHandler())) { - teleport(player, from, to, stack.pop()); + teleport(player, to, stack.pop()); } else { - teleport(player, from, to, Portal.getPortal(target)); + teleport(player, to, Portal.getPortal(target)); } } - private void teleport(Player player, Location from, Location to, Portal target) { + private void teleport(Player player, Location to, Portal target) { if(target == null) { player.sendMessage("teleport " + portal.getId() + " -> UNKNOWN"); return; } player.sendMessage("teleport " + portal.getId() + " -> " + target.getId()); - Vector ownOrientation = portal.getOrientation().clone(); - Vector targetOrientation = target.getOrientation().clone(); - double rotation = Math.atan2(targetOrientation.getZ(), targetOrientation.getX()) - Math.atan2(ownOrientation.getZ(), ownOrientation.getX()); - rotation = Math.atan2(Math.sin(rotation), Math.cos(rotation)); - - Vector pos = to.toVector() - .subtract(portal.getPos1().toVector()).divide(absPlusOne(ownOrientation)) - .rotateAroundY(-rotation) - .multiply(absPlusOne(targetOrientation)).add(target.getPos1().toVector()); - - player.teleport(pos.toLocation(from.getWorld(), (float) (to.getYaw() + Math.toDegrees(rotation)), to.getPitch()), PlayerTeleportEvent.TeleportCause.PLUGIN); - } - - private Vector absPlusOne(Vector v) { - v.setX(Math.abs(v.getX()) + 1); - v.setY(Math.abs(v.getY()) + 1); - v.setZ(Math.abs(v.getZ()) + 1); - return v; + player.teleport(target.denormalize(portal.normalize(to)).toLocation(to.getWorld(), (float) (to.getYaw() - Math.toDegrees(target.getYrotation() - portal.getYrotation())), to.getPitch()), PlayerTeleportEvent.TeleportCause.PLUGIN); } @Override