diff --git a/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java b/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java index c7184c1..fef2f88 100644 --- a/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java +++ b/src/main/java/me/trouper/trimserver/server/commands/AdminCommand.java @@ -3,6 +3,7 @@ package me.trouper.trimserver.server.commands; import io.papermc.paper.registry.keys.TrimPatternKeys; import me.trouper.trimserver.server.systems.AbilityBackend; +import me.trouper.trimserver.utils.SoundPlayer; import me.trouper.trimserver.utils.Text; import me.trouper.trimserver.utils.commands.Args; import me.trouper.trimserver.utils.commands.CommandRegistry; @@ -13,9 +14,7 @@ import me.trouper.trimserver.utils.misc.Randomizer; import me.trouper.trimserver.utils.visual.CustomDisplayRaytracer; import me.trouper.trimserver.utils.visual.DisplayUtils; import net.kyori.adventure.text.format.TextDecoration; -import org.bukkit.Color; -import org.bukkit.Material; -import org.bukkit.Particle; +import org.bukkit.*; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; @@ -31,6 +30,7 @@ import org.bukkit.inventory.meta.trim.TrimPattern; import java.util.List; import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; @CommandRegistry(value = "trims",permission = @Permission("trims.admin"),printStackTrace = true) public class AdminCommand implements QuickCommand { @@ -130,6 +130,22 @@ public class AdminCommand implements QuickCommand { p.getEquipment().setLeggings(createTestArmor(new ItemStack(Material.NETHERITE_LEGGINGS),pattern,material)); p.getEquipment().setBoots(createTestArmor(new ItemStack(Material.NETHERITE_BOOTS),pattern,material)); + for (int i = 0; i < 10; i++) { + double finalI = i; + Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ + DisplayUtils.ring(p.getLocation().clone().add(0, finalI / 5D,0),0.5,Color.RED,0.5F); + },i); + } + + for (int i = 0; i < 10; i++) { + double finalI = i; + Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ + DisplayUtils.ring(p.getLocation().clone().add(0,2,0).subtract(0,finalI / 5D,0),0.5,Color.RED,0.5F); + },10 + i); + } + + new SoundPlayer(p.getLocation(),Sound.BLOCK_BEACON_POWER_SELECT,1,2).play(p); + info(sender,"You now are wearing {0} with {1} {2} trim.", "Netherite",Text.formatEnum(validMaterial),Text.formatEnum(validPattern)); } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java index fc83cb2..3f1756d 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/EyeAbility.java @@ -58,7 +58,7 @@ public class EyeAbility extends AbstractAbility { int maxTick = liv.getMaximumNoDamageTicks(); liv.setNoDamageTicks(0); liv.setMaximumNoDamageTicks(0); - liv.damage(0.5,DamageSource.builder(DamageType.STING).withDamageLocation(owner.getLocation()).withDirectEntity(owner).build()); + liv.damage(0.1,DamageSource.builder(DamageType.STING).withDamageLocation(owner.getLocation()).withDirectEntity(owner).build()); liv.setNoDamageTicks(tick); liv.setMaximumNoDamageTicks(maxTick); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/FlowAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/FlowAbility.java index 814f2fc..bf50398 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/FlowAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/FlowAbility.java @@ -72,6 +72,7 @@ public class FlowAbility extends AbstractAbility { cancel(); return; } + player.closeInventory(); Vector playerDirection = player.getLocation().clone().getDirection().multiply(0.5); breeze.setVelocity(playerDirection); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java index 75d9ba1..72da2b4 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/HostAbility.java @@ -53,7 +53,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean amethystAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -61,7 +61,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean copperAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -69,7 +69,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean diamondAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -77,7 +77,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean emeraldAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -85,7 +85,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean goldAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -93,7 +93,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean ironAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -101,15 +101,15 @@ public class HostAbility extends AbstractAbility { @Override public boolean lapisAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } - @MaterialInfo(name = "Netherite ", description = "Grants true invisibility for 10 seconds and makes your attacks stronger", cooldownTicks = 20 * 15) + @MaterialInfo(name = "Netherite ", description = "Grants true invisibility for 10 seconds and makes your attacks stronger", cooldownTicks = 20 * 20) @Override public boolean netheriteAbility(Player player) { makeInvisible(player,10); - player.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,20*15,1,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,20*6,1,true,false,true)); return true; } @@ -117,7 +117,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean quartzAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -125,7 +125,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean redstoneAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } @@ -133,7 +133,7 @@ public class HostAbility extends AbstractAbility { @Override public boolean resinAbility(Player player) { makeInvisible(player,20); - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*15,0,true,false,true)); + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS,20*6,0,true,false,true)); return true; } } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java index 4be4b83..6a8743e 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/RaiserAbility.java @@ -42,7 +42,7 @@ public class RaiserAbility extends AbstractAbility { double healthThreshold = initialHealth / 2.0; double targetY = initialLocation.getY() + 4.0; int frequency = 2; - int duration = 20 * 30; + int duration = 20 * 7; // --- Start Up Effects --- target.getWorld().playSound(target.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.8f, 1.0f); diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java index 2af4d86..21aa65f 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SentryAbility.java @@ -138,7 +138,7 @@ public class SentryAbility extends AbstractAbility { int maxTick = liv.getMaximumNoDamageTicks(); liv.setNoDamageTicks(0); liv.setMaximumNoDamageTicks(0); - liv.damage(1, DamageSource.builder(DamageType.ARROW).withDirectEntity(dummy).withDamageLocation(finalLoc).build()); + liv.damage(8, DamageSource.builder(DamageType.ARROW).withDirectEntity(dummy).withDamageLocation(finalLoc).build()); liv.setNoDamageTicks(tick); liv.setMaximumNoDamageTicks(maxTick); hitSound.playWithin(20); @@ -206,77 +206,77 @@ public class SentryAbility extends AbstractAbility { @MaterialInfo(name = "Amethyst ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean amethystAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.AMETHYST_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.AMETHYST_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Copper ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean copperAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.COPPER_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.COPPER_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Diamond ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean diamondAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.DIAMOND_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.DIAMOND_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Emerald ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean emeraldAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.EMERALD_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.EMERALD_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Gold ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean goldAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.GOLD_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.GOLD_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Iron ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean ironAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.IRON_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.IRON_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Lapis ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean lapisAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.LAPIS_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.LAPIS_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Netherite Sentry", description = "Spawns a supercharged sentry which absolutely shreds the nearest player", cooldownTicks = 20 * 15) @Override public boolean netheriteAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.NETHERITE_BLOCK,Material.CRAFTER,100,60,1); + spawnSentry(player.getLocation(),player,Material.NETHERITE_BLOCK,Material.CRAFTER,50,60,2); return true; } @MaterialInfo(name = "Quartz ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean quartzAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.QUARTZ_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.QUARTZ_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Redstone ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean redstoneAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.REDSTONE_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.REDSTONE_BLOCK,Material.DISPENSER,25,60,5); return true; } @MaterialInfo(name = "Resin ", description = "Spawns a sentry which shoots the nearest player", cooldownTicks = 20 * 10) @Override public boolean resinAbility(Player player) { - spawnSentry(player.getLocation(),player,Material.RESIN_BLOCK,Material.DISPENSER,50,60,2); + spawnSentry(player.getLocation(),player,Material.RESIN_BLOCK,Material.DISPENSER,25,60,5); return true; } } diff --git a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java index b6dea3b..9c8a3bc 100644 --- a/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java +++ b/src/main/java/me/trouper/trimserver/server/systems/abilities/trims/SilenceAbility.java @@ -54,7 +54,7 @@ public class SilenceAbility extends AbstractAbility { Optional target = TargetingUtils.getClosestPlayer(point.getLoc(),1,entity -> !entity.equals(player) && !entity.isDead() && !main.man().trustBackend.trusts(player,entity) && !shaper.activeShellTasks.containsKey(entity.getUniqueId())); target.ifPresent(value -> PlayerUtils.dealTrueDamage(value, DamageSource.builder(DamageType.SONIC_BOOM).withDirectEntity(player).build(), 10)); Verbose.send("Traced warden beam:"); - return target.isPresent() || !point.getBlock().isPassable(); + return target.isPresent(); },(point,blockHit) -> { Verbose.send("Block was hit at %s. Return Value !Passable: %s",blockHit.getLocation(),!blockHit.isPassable()); return !blockHit.isPassable(); diff --git a/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayRaytracer.java b/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayRaytracer.java index ffd0d70..1b638a6 100644 --- a/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayRaytracer.java +++ b/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayRaytracer.java @@ -1,6 +1,7 @@ package me.trouper.trimserver.utils.visual; import me.trouper.trimserver.utils.Verbose; +import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -10,9 +11,12 @@ import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.BoundingBox; +import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; import org.bukkit.util.VoxelShape; +import java.util.ArrayList; +import java.util.List; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -159,80 +163,34 @@ public class CustomDisplayRaytracer { Predicate hitCondition) { return traceDelayed(plugin, start, direction, distance, 0.5, tickDelay,1, hitCondition); } - - public static Point traceWithReflection(Location start, Vector direction, double distance, double interval, - int maxReflections, Predicate hitCondition, - BiPredicate blockReflectCondition, - BiPredicate entityReflectCondition) { - if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); - if (distance <= 0) throw new IllegalArgumentException("distance cannot be zero or negative!"); - if (maxReflections < 0) throw new IllegalArgumentException("maxReflections cannot be negative!"); - Vector normalizedDir = direction.clone().normalize(); - Location currentStart = start.clone(); - Vector currentDir = normalizedDir.clone(); - double remainingDistance = distance; + private static BlockFace traceBlockFace(Location startLocation, Vector direction, double maxDistance) { + Predicate blockPredicate = block -> true; + Predicate entityPredicate = entity -> false; - for (int reflection = 0; reflection <= maxReflections; reflection++) { - Point hitPoint = trace(currentStart, currentDir, remainingDistance, interval, hitCondition); + RayTraceResult result = startLocation.getWorld().rayTrace(startLocation, direction, maxDistance, FluidCollisionMode.NEVER,true,0.1,entityPredicate, blockPredicate); - if (hitPoint.wasMissed()) { - Verbose.send("First ray missed, returning."); - return hitPoint; - } - - double distanceUsed = currentStart.distance(hitPoint.getLoc()); - remainingDistance -= distanceUsed; - - if (remainingDistance <= 0) { - Verbose.send("First ray too long, returning."); - return hitPoint; - } - Vector reflectedDirection = null; - - if (HIT_BLOCK.test(hitPoint)) { - Verbose.send("hit block!"); - Block hitBlock = hitPoint.getBlock(); - - if (blockReflectCondition.test(hitPoint,hitBlock)) { - Verbose.send("Check passed!"); - BlockFace hitFace = hitPoint.getBlockFace(currentDir); - if (hitFace != null) { - Verbose.send("Face not null"); - reflectedDirection = calculateBlockReflection(currentDir, hitFace); - } - } - } - - else if (HIT_ENTITY.test(hitPoint)) { - Verbose.send("hit entity!"); - Entity hitEntity = hitPoint.getNearbyEntities(null, 5, true, 0.1, - e -> e instanceof LivingEntity le && !le.isDead()).stream().findFirst().orElse(null); - - if (hitEntity != null && entityReflectCondition != null && entityReflectCondition.test(hitPoint,hitEntity)) { - Verbose.send("Check passed!"); - reflectedDirection = currentDir.clone().multiply(-1).normalize(); - } - } - - if (reflectedDirection == null) { - Verbose.send("Reflected direction null"); - return hitPoint; - } - - currentStart = hitPoint.getLoc().clone(); - currentDir = reflectedDirection; - - currentStart = blocksInFrontOf(currentStart,currentDir,interval*2,false).getLoc(); + if (result != null && result.getHitBlock() != null && result.getHitBlockFace() != null) { + return result.getHitBlockFace(); } - return trace(currentStart, currentDir, remainingDistance, interval, hitCondition); + return null; } - - private static Vector calculateBlockReflection(Vector incident, BlockFace face) { + + private static Vector calculateReflection(Vector incident, BlockFace face) { Verbose.send("Calculating vector reflection for face %s".formatted(face)); Vector normal = getFaceNormal(face); + return calculateReflection(incident,normal); + } + + private static Vector calculateReflection(Vector incident, Vector normal) { + Verbose.send("Calculating entity reflection"); + + if (normal.lengthSquared() < 0.001) { + return incident.clone().multiply(-1).normalize(); + } + // r = i - 2(i dot n)n double dot = incident.dot(normal); Vector reflection = incident.clone().subtract(normal.clone().multiply(2 * dot)); diff --git a/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayReflector.java.disabled b/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayReflector.java.disabled deleted file mode 100644 index 035f2db..0000000 --- a/src/main/java/me/trouper/trimserver/utils/visual/CustomDisplayReflector.java.disabled +++ /dev/null @@ -1,495 +0,0 @@ -package me.trouper.trimserver.utils.visual; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; -import org.bukkit.util.Vector; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.Predicate; - -public class CustomDisplayReflector { - - // Commonly used reflection conditions - public static final BiPredicate REFLECT_ON_ANY_BLOCK = (point, face) -> - face != null && !point.getBlock().isEmpty() && point.getBlock().isCollidable(); - - public static final BiPredicate REFLECT_ON_ANY_ENTITY = (point, entity) -> - entity instanceof LivingEntity && !entity.isDead(); - - /** - * Creates a reflection condition that excludes a specific entity - * @param exclude The entity to exclude from reflection - * @return A BiPredicate that determines if reflection should occur on an entity - */ - public static BiPredicate reflectOnEntityExclude(Entity exclude) { - return (point, entity) -> entity != exclude && entity instanceof LivingEntity && !entity.isDead(); - } - - /** - * Traces a ray that can reflect off blocks based on provided conditions - * @param start The starting location - * @param direction The initial direction - * @param maxDistance The maximum total distance the ray can travel - * @param maxReflections The maximum number of reflections allowed - * @param interval The interval between trace points - * @param blockReflectCondition Determines if a ray should reflect off a block at a given point - * @return List of ray segments (each representing a segment between reflections) - */ - public static List traceReflectingRay( - Location start, - Vector direction, - double maxDistance, - int maxReflections, - double interval, - BiPredicate blockReflectCondition) { - - if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); - if (maxDistance <= 0) throw new IllegalArgumentException("maxDistance cannot be zero or negative!"); - - List segments = new ArrayList<>(); - Vector normalizedDir = direction.clone().normalize(); - Location currentStart = start.clone(); - double remainingDistance = maxDistance; - int reflections = 0; - - while (remainingDistance > 0 && reflections <= maxReflections) { - // Trace until hit or max distance - RayResult result = traceUntilReflect(currentStart, normalizedDir, remainingDistance, interval, blockReflectCondition); - - // Add segment - segments.add(new RaySegment(currentStart.clone(), result.getEndPoint().getLoc().clone(), normalizedDir.clone(), result.getDistance())); - - // Check if we've hit something reflective - if (result.getReflectionType() == ReflectionType.NONE) { - break; // No reflection occurred, end of ray - } - - remainingDistance -= result.getDistance(); - reflections++; - - // Update for next segment - currentStart = result.getEndPoint().getLoc().clone(); - normalizedDir = result.getNewDirection().clone(); - } - - return segments; - } - - /** - * Traces a ray that can reflect off both blocks and entities - * @param start The starting location - * @param direction The initial direction - * @param maxDistance The maximum total distance the ray can travel - * @param maxReflections The maximum number of reflections allowed - * @param interval The interval between trace points - * @param blockReflectCondition Determines if a ray should reflect off a block - * @param entityReflectCondition Determines if a ray should reflect off an entity - * @return List of ray segments (each representing a segment between reflections) - */ - public static List traceReflectingRay( - Location start, - Vector direction, - double maxDistance, - int maxReflections, - double interval, - BiPredicate blockReflectCondition, - BiPredicate entityReflectCondition) { - - if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); - if (maxDistance <= 0) throw new IllegalArgumentException("maxDistance cannot be zero or negative!"); - - List segments = new ArrayList<>(); - Vector normalizedDir = direction.clone().normalize(); - Location currentStart = start.clone(); - double remainingDistance = maxDistance; - int reflections = 0; - - while (remainingDistance > 0 && reflections <= maxReflections) { - // Trace until hit or max distance - RayResult result = traceUntilReflect(currentStart, normalizedDir, remainingDistance, interval, blockReflectCondition, entityReflectCondition); - - // Add segment - segments.add(new RaySegment(currentStart.clone(), result.getEndPoint().getLoc().clone(), normalizedDir.clone(), result.getDistance())); - - // Check if we've hit something reflective - if (result.getReflectionType() == ReflectionType.NONE) { - break; // No reflection occurred, end of ray - } - - remainingDistance -= result.getDistance(); - reflections++; - - // Update for next segment - currentStart = result.getEndPoint().getLoc().clone(); - normalizedDir = result.getNewDirection().clone(); - } - - return segments; - } - - /** - * Traces a ray until reflection or max distance - */ - private static RayResult traceUntilReflect( - Location start, - Vector direction, - double maxDistance, - double interval, - BiPredicate blockReflectCondition) { - - Vector normalizedDir = direction.clone().normalize(); - - for (double i = 0.0; i < maxDistance; i += interval) { - Point point = CustomDisplayRaytracer.blocksInFrontOf(start, normalizedDir, i, false); - - // Check for block reflection - BlockFace hitFace = getHitBlockFace(point); - if (hitFace != null && blockReflectCondition.test(point, hitFace)) { - // Calculate reflection - Vector reflectedDir = calculateBlockReflection(normalizedDir, hitFace); - return new RayResult(point, reflectedDir, i, ReflectionType.BLOCK, hitFace, null); - } - } - - // No reflection found, return end point - Point endPoint = CustomDisplayRaytracer.blocksInFrontOf(start, normalizedDir, maxDistance, true); - return new RayResult(endPoint, normalizedDir, maxDistance, ReflectionType.NONE, null, null); - } - - /** - * Traces a ray until reflection off block or entity, or max distance - */ - private static RayResult traceUntilReflect( - Location start, - Vector direction, - double maxDistance, - double interval, - BiPredicate blockReflectCondition, - BiPredicate entityReflectCondition) { - - Vector normalizedDir = direction.clone().normalize(); - - for (double i = 0.0; i < maxDistance; i += interval) { - Point point = CustomDisplayRaytracer.blocksInFrontOf(start, normalizedDir, i, false); - - // Check for entity reflection first - List entities = point.getNearbyEntities(null, 5, true, 0.1, e -> true); - for (Entity entity : entities) { - if (entityReflectCondition.test(point, entity)) { - // Entity reflection - return in opposite direction - Vector reflectedDir = normalizedDir.clone().multiply(-1); - return new RayResult(point, reflectedDir, i, ReflectionType.ENTITY, null, entity); - } - } - - // Check for block reflection - BlockFace hitFace = getHitBlockFace(point); - if (hitFace != null && blockReflectCondition.test(point, hitFace)) { - Vector reflectedDir = calculateBlockReflection(normalizedDir, hitFace); - return new RayResult(point, reflectedDir, i, ReflectionType.BLOCK, hitFace, null); - } - } - - // No reflection found, return end point - Point endPoint = CustomDisplayRaytracer.blocksInFrontOf(start, normalizedDir, maxDistance, true); - return new RayResult(endPoint, normalizedDir, maxDistance, ReflectionType.NONE, null, null); - } - - /** - * Performs a delayed tracing of a reflecting ray - */ - public static BukkitTask traceReflectingRayDelayed( - Plugin plugin, - Location start, - Vector direction, - double maxDistance, - int maxReflections, - double interval, - long tickDelay, - BiPredicate blockReflectCondition, - Function segmentAction) { - - if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); - if (maxDistance <= 0) throw new IllegalArgumentException("maxDistance cannot be zero or negative!"); - if (tickDelay < 0) throw new IllegalArgumentException("tickDelay cannot be negative!"); - - Vector normalizedDir = direction.clone().normalize(); - - return new BukkitRunnable() { - private Location currentStart = start.clone(); - private Vector currentDir = normalizedDir.clone(); - private double remainingDistance = maxDistance; - private int reflections = 0; - private double currentSegmentDistance = 0; - - @Override - public void run() { - if (remainingDistance <= 0 || reflections > maxReflections) { - this.cancel(); - return; - } - - Point point = CustomDisplayRaytracer.blocksInFrontOf( - currentStart, currentDir, currentSegmentDistance, false); - - // Check for block reflection - BlockFace hitFace = getHitBlockFace(point); - if (hitFace != null && blockReflectCondition.test(point, hitFace)) { - // Create segment - RaySegment segment = new RaySegment( - currentStart.clone(), - point.getLoc().clone(), - currentDir.clone(), - currentSegmentDistance); - - // Execute action (if returns false, stop the ray) - if (!segmentAction.apply(segment)) { - this.cancel(); - return; - } - - // Update for next segment - currentStart = point.getLoc().clone(); - currentDir = calculateBlockReflection(currentDir, hitFace); - remainingDistance -= currentSegmentDistance; - reflections++; - currentSegmentDistance = 0; - return; - } - - // Increment distance for this step - currentSegmentDistance += interval; - - // Check if we've reached end of current segment without reflection - if (currentSegmentDistance > remainingDistance) { - Point endPoint = CustomDisplayRaytracer.blocksInFrontOf( - currentStart, currentDir, remainingDistance, true); - - RaySegment segment = new RaySegment( - currentStart.clone(), - endPoint.getLoc().clone(), - currentDir.clone(), - remainingDistance); - - segmentAction.apply(segment); - this.cancel(); - } - } - }.runTaskTimer(plugin, 0L, tickDelay); - } - - /** - * Performs a delayed tracing of a reflecting ray with both block and entity reflections - */ - public static BukkitTask traceReflectingRayDelayed( - Plugin plugin, - Location start, - Vector direction, - double maxDistance, - int maxReflections, - double interval, - long tickDelay, - BiPredicate blockReflectCondition, - BiPredicate entityReflectCondition, - Function segmentAction) { - - if (interval <= 0) throw new IllegalArgumentException("interval cannot be zero or negative!"); - if (maxDistance <= 0) throw new IllegalArgumentException("maxDistance cannot be zero or negative!"); - if (tickDelay < 0) throw new IllegalArgumentException("tickDelay cannot be negative!"); - - Vector normalizedDir = direction.clone().normalize(); - - return new BukkitRunnable() { - private Location currentStart = start.clone(); - private Vector currentDir = normalizedDir.clone(); - private double remainingDistance = maxDistance; - private int reflections = 0; - private double currentSegmentDistance = 0; - - @Override - public void run() { - if (remainingDistance <= 0 || reflections > maxReflections) { - this.cancel(); - return; - } - - Point point = CustomDisplayRaytracer.blocksInFrontOf( - currentStart, currentDir, currentSegmentDistance, false); - - // Check for entity reflection - List entities = point.getNearbyEntities(null, 5, true, 0.1, e -> true); - for (Entity entity : entities) { - if (entityReflectCondition.test(point, entity)) { - // Create segment - RaySegment segment = new RaySegment( - currentStart.clone(), - point.getLoc().clone(), - currentDir.clone(), - currentSegmentDistance); - - // Execute action (if returns false, stop the ray) - if (!segmentAction.apply(segment)) { - this.cancel(); - return; - } - - // Update for next segment - reverse direction for entity reflection - currentStart = point.getLoc().clone(); - currentDir = currentDir.clone().multiply(-1); - remainingDistance -= currentSegmentDistance; - reflections++; - currentSegmentDistance = 0; - return; - } - } - - // Check for block reflection - BlockFace hitFace = getHitBlockFace(point); - if (hitFace != null && blockReflectCondition.test(point, hitFace)) { - // Create segment - RaySegment segment = new RaySegment( - currentStart.clone(), - point.getLoc().clone(), - currentDir.clone(), - currentSegmentDistance); - - // Execute action (if returns false, stop the ray) - if (!segmentAction.apply(segment)) { - this.cancel(); - return; - } - - // Update for next segment - currentStart = point.getLoc().clone(); - currentDir = calculateBlockReflection(currentDir, hitFace); - remainingDistance -= currentSegmentDistance; - reflections++; - currentSegmentDistance = 0; - return; - } - - // Increment distance for this step - currentSegmentDistance += interval; - - // Check if we've reached end of current segment without reflection - if (currentSegmentDistance > remainingDistance) { - Point endPoint = CustomDisplayRaytracer.blocksInFrontOf( - currentStart, currentDir, remainingDistance, true); - - RaySegment segment = new RaySegment( - currentStart.clone(), - endPoint.getLoc().clone(), - currentDir.clone(), - remainingDistance); - - segmentAction.apply(segment); - this.cancel(); - } - } - }.runTaskTimer(plugin, 0L, tickDelay); - } - - - - /** - * Represents a segment of a reflected ray - */ - public static class RaySegment { - private final Location start; - private final Location end; - private final Vector direction; - private final double distance; - - public RaySegment(Location start, Location end, Vector direction, double distance) { - this.start = start; - this.end = end; - this.direction = direction; - this.distance = distance; - } - - public Location getStart() { - return start; - } - - public Location getEnd() { - return end; - } - - public Vector getDirection() { - return direction; - } - - public double getDistance() { - return distance; - } - - public World getWorld() { - return start.getWorld(); - } - } - - /** - * Types of reflections that can occur - */ - public enum ReflectionType { - NONE, - BLOCK, - ENTITY - } - - /** - * Result of a ray trace including reflection information - */ - private static class RayResult { - private final Point endPoint; - private final Vector newDirection; - private final double distance; - private final ReflectionType reflectionType; - private final BlockFace hitFace; - private final Entity hitEntity; - - public RayResult(Point endPoint, Vector newDirection, double distance, - ReflectionType reflectionType, BlockFace hitFace, Entity hitEntity) { - this.endPoint = endPoint; - this.newDirection = newDirection; - this.distance = distance; - this.reflectionType = reflectionType; - this.hitFace = hitFace; - this.hitEntity = hitEntity; - } - - public Point getEndPoint() { - return endPoint; - } - - public Vector getNewDirection() { - return newDirection; - } - - public double getDistance() { - return distance; - } - - public ReflectionType getReflectionType() { - return reflectionType; - } - - public BlockFace getHitFace() { - return hitFace; - } - - public Entity getHitEntity() { - return hitEntity; - } - } -} \ No newline at end of file diff --git a/src/main/java/me/trouper/trimserver/utils/visual/Point.java b/src/main/java/me/trouper/trimserver/utils/visual/Point.java index 6b54899..ec994ca 100644 --- a/src/main/java/me/trouper/trimserver/utils/visual/Point.java +++ b/src/main/java/me/trouper/trimserver/utils/visual/Point.java @@ -19,6 +19,7 @@ public class Point { private final Block block; private final boolean missed; private final double traveledDist; + private List rayPath; public Point(Location loc, double traveledDist, boolean missed) { this.loc = loc; @@ -118,4 +119,16 @@ public class Point { public double distance(Location other) { return other.distance(loc); } + + public Point addRayPoint(Point point) { + rayPath.add(point); + return point; + } + + public List setRayPath(List points) { + rayPath = points; + return rayPath; + } + + public List getRayPath = new ArrayList<>(); } diff --git a/src/main/java/me/trouper/trimserver/utils/visual/ReflectionResult.java b/src/main/java/me/trouper/trimserver/utils/visual/ReflectionResult.java new file mode 100644 index 0000000..81198ac --- /dev/null +++ b/src/main/java/me/trouper/trimserver/utils/visual/ReflectionResult.java @@ -0,0 +1,27 @@ +package me.trouper.trimserver.utils.visual; + +import org.bukkit.util.Vector; + +public class ReflectionResult { + private final Point hitPoint; + private final boolean shouldReflect; + private final Vector reflectedDirection; + + public ReflectionResult(Point hitPoint, boolean shouldReflect, Vector reflectedDirection) { + this.hitPoint = hitPoint; + this.shouldReflect = shouldReflect; + this.reflectedDirection = reflectedDirection; + } + + public Point getHitPoint() { + return hitPoint; + } + + public boolean shouldReflect() { + return shouldReflect; + } + + public Vector getReflectedDirection() { + return reflectedDirection; + } +}