package dev.loki.loparkour.world; import dev.loki.loparkour.LoParkour; import dev.loki.loparkour.config.Option; import dev.loki.loparkour.session.Session; import dev.loki.loparkour.util.Locations; import org.bukkit.Location; import java.util.HashMap; import java.util.Map; import java.util.stream.IntStream; /** *

Divides the parkour world in sections, each with an active session.

*

Iteration 2.

* * @author loki * @since 5.0.0 */ public class Divider { /** * Map with all session ids map to the session instances. */ public static final Map sections = new HashMap<>(); /** * Associates a session to a specific section. * * @param session The session. */ public static synchronized Location add(Session session) { // attempts to get the closest available section to the center var missing = IntStream.range(0, sections.size() + 1) .filter(i -> !sections.containsValue(i)) .findFirst() .orElseThrow(); sections.put(session, missing); var location = toLocation(session); LoParkour.log("Added session at %s".formatted(Locations.toString(location, true))); return location; } /** * Disassociates a session from a specific section. * * @param session The session. */ public static void remove(Session session) { LoParkour.log("Removed session at %s".formatted(Locations.toString(toLocation(session), true))); sections.remove(session); } /** * @param session The session. * @return The location at the center of section n. */ private static Location toLocation(Session session) { int[] xz = spiralAt(sections.get(session)); return new Location(World.getWorld(), xz[0] * Option.BORDER_SIZE, (Option.MAX_Y + Option.MIN_Y) / 2.0, xz[1] * Option.BORDER_SIZE); } /** * @param session The session. * @return Array where the first item is the smallest location and second item is the largest. */ public static Location[] toSelection(Session session) { Location center = toLocation(session); // get the min and max locations Location max = center.clone().add(Option.BORDER_SIZE / 2, 0, Option.BORDER_SIZE / 2); Location min = center.clone().subtract(Option.BORDER_SIZE / 2, 0, Option.BORDER_SIZE / 2); max.setY(Option.MAX_Y); min.setY(Option.MIN_Y); return new Location[]{min, max}; } /** * Gets a spiral * * @param n The number of value * @return the coords of this value */ // https://math.stackexchange.com/a/163101 private static int[] spiralAt(int n) { if (n < 0) { throw new IllegalArgumentException("Invalid n bound: %d".formatted(n)); } n++; // one-index int k = (int) Math.ceil((Math.sqrt(n) - 1) / 2); int t = 2 * k + 1; int m = t * t; t--; if (n > m - t) { return new int[]{k - (m - n), -k}; } else { m -= t; } if (n > m - t) { return new int[]{-k, -k + (m - n)}; } else { m -= t; } if (n > m - t) { return new int[]{-k + (m - n), k}; } else { return new int[]{k, k - (m - n - t)}; } } }