diff --git a/src/main/java/dev/loki/util/MathUtil.java b/src/main/java/dev/loki/util/MathUtil.java new file mode 100644 index 0000000..67cc831 --- /dev/null +++ b/src/main/java/dev/loki/util/MathUtil.java @@ -0,0 +1,165 @@ +package dev.loki.util; + +import org.bukkit.Location; +import org.bukkit.util.Vector; + +public final class MathUtil { + + private MathUtil() {} + + public static int clamp(int value, int min, int max) { + return Math.max(min, Math.min(max, value)); + } + + public static long clamp(long value, long min, long max) { + return Math.max(min, Math.min(max, value)); + } + + public static float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } + + public static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + + public static double lerp(double start, double end, double t) { + t = clamp(t, 0.0, 1.0); + return start + (end - start) * t; + } + + public static float lerp(float start, float end, float t) { + t = clamp(t, 0.0f, 1.0f); + return start + (end - start) * t; + } + + public static Vector lerp(Vector start, Vector end, double t) { + t = clamp(t, 0.0, 1.0); + return start.clone().multiply(1 - t).add(end.clone().multiply(t)); + } + + public static Location lerp(Location start, Location end, double t) { + t = clamp(t, 0.0, 1.0); + double x = lerp(start.getX(), end.getX(), t); + double y = lerp(start.getY(), end.getY(), t); + double z = lerp(start.getZ(), end.getZ(), t); + float yaw = lerp(start.getYaw(), end.getYaw(), (float) t); + float pitch = lerp(start.getPitch(), end.getPitch(), (float) t); + return new Location(start.getWorld(), x, y, z, yaw, pitch); + } + + public static double inverseLerp(double start, double end, double value) { + if (end == start) return 0; + return clamp((value - start) / (end - start), 0.0, 1.0); + } + + public static double map(double value, double inMin, double inMax, double outMin, double outMax) { + double t = inverseLerp(inMin, inMax, value); + return lerp(outMin, outMax, t); + } + + public static double smoothStep(double edge0, double edge1, double x) { + double t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); + } + + public static double smootherStep(double edge0, double edge1, double x) { + double t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * t * (t * (t * 6 - 15) + 10); + } + + public static int wrap(int value, int min, int max) { + int range = max - min; + if (range == 0) return min; + return ((value - min) % range + range) % range + min; + } + + public static double wrap(double value, double min, double max) { + double range = max - min; + if (range == 0) return min; + return ((value - min) % range + range) % range + min; + } + + public static double normalizeAngle(double angle) { + return wrap(angle, -180.0, 180.0); + } + + public static double angleDifference(double angle1, double angle2) { + double diff = normalizeAngle(angle2 - angle1); + return Math.abs(diff); + } + + public static boolean isInRange(double value, double min, double max) { + return value >= min && value <= max; + } + + public static boolean isInRange(int value, int min, int max) { + return value >= min && value <= max; + } + + public static int roundUp(int value, int multiple) { + if (multiple == 0) return value; + return ((value + multiple - 1) / multiple) * multiple; + } + + public static double roundToDecimals(double value, int decimals) { + double factor = Math.pow(10, decimals); + return Math.round(value * factor) / factor; + } + + public static double distanceSquared(double x1, double y1, double z1, double x2, double y2, double z2) { + double dx = x2 - x1; + double dy = y2 - y1; + double dz = z2 - z1; + return dx * dx + dy * dy + dz * dz; + } + + public static double horizontalDistanceSquared(double x1, double z1, double x2, double z2) { + double dx = x2 - x1; + double dz = z2 - z1; + return dx * dx + dz * dz; + } + + public static int sign(double value) { + if (value > 0) return 1; + if (value < 0) return -1; + return 0; + } + + public static double approach(double current, double target, double maxDelta) { + if (current < target) { + return Math.min(current + maxDelta, target); + } + return Math.max(current - maxDelta, target); + } + + public static float approach(float current, float target, float maxDelta) { + if (current < target) { + return Math.min(current + maxDelta, target); + } + return Math.max(current - maxDelta, target); + } + + public static double pingPong(double value, double length) { + return length - Math.abs(wrap(value, -length, length)); + } + + public static double toRadians(double degrees) { + return Math.toRadians(degrees); + } + + public static double toDegrees(double radians) { + return Math.toDegrees(radians); + } + + public static Vector rotateVector(Vector vector, double yawDegrees) { + double yaw = Math.toRadians(yawDegrees); + double cos = Math.cos(yaw); + double sin = Math.sin(yaw); + + double x = vector.getX() * cos - vector.getZ() * sin; + double z = vector.getX() * sin + vector.getZ() * cos; + + return new Vector(x, vector.getY(), z); + } +} diff --git a/src/main/java/dev/loki/util/ParticleUtil.java b/src/main/java/dev/loki/util/ParticleUtil.java new file mode 100644 index 0000000..7979797 --- /dev/null +++ b/src/main/java/dev/loki/util/ParticleUtil.java @@ -0,0 +1,204 @@ +package dev.loki.util; + +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.List; + +public final class ParticleUtil { + + private static final int DEFAULT_COUNT = 1; + private static final double DEFAULT_SPREAD = 0.0; + private static final float DEFAULT_SIZE = 1.0f; + + private ParticleUtil() {} + + public static void spawn(Particle particle, Location location) { + spawn(particle, location, DEFAULT_COUNT); + } + + public static void spawn(Particle particle, Location location, int count) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(particle, location, count); + } + + public static void spawn(Particle particle, Location location, int count, double spreadX, double spreadY, double spreadZ) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ); + } + + public static void spawn(Particle particle, Location location, int count, double spreadX, double spreadY, double spreadZ, double speed) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ, speed); + } + + public static void spawnDust(Location location, Color color, float size) { + World world = location.getWorld(); + if (world == null) return; + Particle.DustOptions dustOptions = new Particle.DustOptions(color, size); + world.spawnParticle(Particle.DUST, location, 1, dustOptions); + } + + public static void spawnDust(Location location, Color color, float size, int count) { + World world = location.getWorld(); + if (world == null) return; + Particle.DustOptions dustOptions = new Particle.DustOptions(color, size); + world.spawnParticle(Particle.DUST, location, count, 0.1, 0.1, 0.1, 0, dustOptions); + } + + public static void spawnToPlayer(Particle particle, Player player, Location location, int count) { + player.spawnParticle(particle, location, count); + } + + public static void spawnToPlayer(Particle particle, Player player, Location location, int count, double spreadX, double spreadY, double spreadZ) { + player.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ); + } + + public static void drawLine(Particle particle, Location start, Location end, int points) { + if (!start.getWorld().equals(end.getWorld())) return; + + Vector direction = end.toVector().subtract(start.toVector()); + double distance = direction.length(); + direction.normalize(); + + double step = distance / points; + Location current = start.clone(); + + for (int i = 0; i <= points; i++) { + spawn(particle, current); + current.add(direction.clone().multiply(step)); + } + } + + public static void drawCircle(Particle particle, Location center, double radius, int points) { + World world = center.getWorld(); + if (world == null) return; + + for (int i = 0; i < points; i++) { + double angle = 2 * Math.PI * i / points; + double x = center.getX() + radius * Math.cos(angle); + double z = center.getZ() + radius * Math.sin(angle); + Location point = new Location(world, x, center.getY(), z); + spawn(particle, point); + } + } + + public static void drawCircle(Particle particle, Location center, double radius, int points, Color color, float size) { + World world = center.getWorld(); + if (world == null) return; + + for (int i = 0; i < points; i++) { + double angle = 2 * Math.PI * i / points; + double x = center.getX() + radius * Math.cos(angle); + double z = center.getZ() + radius * Math.sin(angle); + Location point = new Location(world, x, center.getY(), z); + spawnDust(point, color, size); + } + } + + public static void drawSphere(Particle particle, Location center, double radius, int points) { + World world = center.getWorld(); + if (world == null) return; + + for (int i = 0; i < points; i++) { + double theta = Math.random() * 2 * Math.PI; + double phi = Math.random() * Math.PI; + double x = center.getX() + radius * Math.sin(phi) * Math.cos(theta); + double y = center.getY() + radius * Math.cos(phi); + double z = center.getZ() + radius * Math.sin(phi) * Math.sin(theta); + Location point = new Location(world, x, y, z); + spawn(particle, point); + } + } + + public static void drawBox(Particle particle, Location min, Location max, double step) { + if (!min.getWorld().equals(max.getWorld())) return; + World world = min.getWorld(); + if (world == null) return; + + for (double x = min.getX(); x <= max.getX(); x += step) { + for (double y = min.getY(); y <= max.getY(); y += step) { + for (double z = min.getZ(); z <= max.getZ(); z += step) { + boolean edge = (x <= min.getX() + step || x >= max.getX() - step) || + (y <= min.getY() + step || y >= max.getY() - step) || + (z <= min.getZ() + step || z >= max.getZ() - step); + if (edge) { + spawn(particle, new Location(world, x, y, z)); + } + } + } + } + } + + public static void burst(Particle particle, Location center, int count, double speed) { + World world = center.getWorld(); + if (world == null) return; + world.spawnParticle(particle, center, count, 0.5, 0.5, 0.5, speed); + } + + public static void burstToPlayers(Particle particle, List players, Location center, int count, double speed) { + for (Player player : players) { + player.spawnParticle(particle, center, count, 0.5, 0.5, 0.5, speed); + } + } + + public static void trail(Particle particle, Location start, Vector direction, int length, double spacing) { + Location current = start.clone(); + Vector step = direction.clone().normalize().multiply(spacing); + + for (int i = 0; i < length; i++) { + spawn(particle, current); + current.add(step); + } + } + + public static void shockwave(Particle particle, Location center, double maxRadius, int rings, double speed) { + World world = center.getWorld(); + if (world == null) return; + + for (int ring = 1; ring <= rings; ring++) { + double radius = (maxRadius / rings) * ring; + int points = (int) (16 * radius); + drawCircle(particle, center, radius, points); + } + } + + public static void damageIndicator(Location location) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(Particle.DAMAGE_INDICATOR, location, 10, 0.3, 0.3, 0.3, 0); + } + + public static void blood(Location location) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(Particle.BLOCK_CRUMBLE, location, 20, 0.2, 0.2, 0.2, 0, + org.bukkit.Material.REDSTONE_BLOCK.createBlockData()); + } + + public static void sparks(Location location) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(Particle.ELECTRIC_SPARK, location, 5, 0.1, 0.1, 0.1, 0.1); + } + + public static void heal(Location location) { + World world = location.getWorld(); + if (world == null) return; + spawnDust(location, Color.LIME, 1.0f, 10); + } + + public static void teleportEffect(Location location) { + World world = location.getWorld(); + if (world == null) return; + world.spawnParticle(Particle.PORTAL, location, 100, 0.5, 1.0, 0.5, 1.0); + world.spawnParticle(Particle.REVERSE_PORTAL, location, 50, 0.3, 0.5, 0.3, 0.5); + } +}