diff --git a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpCalculator.java b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpCalculator.java index 3d65722..fdd2e74 100644 --- a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpCalculator.java +++ b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpCalculator.java @@ -1,6 +1,5 @@ package dev.loki.loparkour.generator.jump.calculation; -import dev.loki.loparkour.config.options.Option; import dev.loki.loparkour.generator.core.model.GeneratorOption; import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator; import dev.loki.loparkour.generator.jump.placement.BlockSelector; @@ -100,7 +99,10 @@ public class JumpCalculator { @NotNull private Vector calculateJumpOffset(int distance, int height) { double sd = java.util.Arrays.asList(generator.generatorOptions).contains(GeneratorOption.REDUCE_RANDOM_BLOCK_SELECTION_ANGLE) ? 0.5 : 1; - int randomOffset = new JumpOffsetGenerator(height, distance).getRandomOffset(0, sd); + int randomOffset = Math.min( + new JumpOffsetGenerator(height, distance).getRandomOffset(0, sd), + (int) Math.floor(Math.sqrt(JumpValidator.MAX_HORIZONTAL_DISTANCE * JumpValidator.MAX_HORIZONTAL_DISTANCE - distance * distance)) + ); Vector offset = generator.state.heading.clone().multiply(distance).setY(height); if (offset.getX() == 0) { @@ -109,7 +111,6 @@ public class JumpCalculator { offset.setZ(randomOffset); } - offset.rotateAroundY(angleInY(generator.state.heading, Option.HEADING.getDirection())); return offset; } @@ -121,7 +122,7 @@ public class JumpCalculator { while (!validator.canJump(current.getLocation(), candidate.getLocation()) && attempts < 10) { // Reduce distance and height to make jump easier distance = Math.max(1, distance - 1); - height = Math.max(-1, height - 1); + height = Math.max(-2, height - 1); Vector offset = calculateJumpOffset(distance, height); candidate = current.getLocation().add(offset).getBlock(); @@ -150,11 +151,7 @@ public class JumpCalculator { default -> 3; // Default restriction }; } - - private double angleInY(@NotNull Vector a, @NotNull Vector b) { - return Math.atan2(b.getZ(), b.getX()) - Math.atan2(a.getZ(), a.getX()); - } - + /** * Represents jump constraints for a specific block type. */ diff --git a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpDirector.java b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpDirector.java index 93f7d76..bcc9b50 100644 --- a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpDirector.java +++ b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpDirector.java @@ -80,30 +80,43 @@ public class JumpDirector { double tz = progress[2][0]; double borderMarginZ = progress[2][1]; - Vector recommendedHeading = new Vector(0, 0, 0); - // check border + // Determine per-axis corrections needed + boolean needX = false; + boolean needZ = false; + double dirX = 0; + double dirZ = 0; + if (tx < borderMarginX) { - // x should increase - recommendedHeading = new Vector(1, 0, 1); + needX = true; + dirX = 1; } else if (tx > 1 - borderMarginX) { - // x should decrease - recommendedHeading = new Vector(-1, 0, -1); + needX = true; + dirX = -1; } if (tz < borderMarginZ) { - // z should increase - recommendedHeading = new Vector(1, 0, 1); + needZ = true; + dirZ = 1; } else if (tz > 1 - borderMarginZ) { - // z should decrease - recommendedHeading = new Vector(-1, 0, -1); + needZ = true; + dirZ = -1; } - if (recommendedHeading.lengthSquared() == 0) { + if (!needX && !needZ) { return current; - } else { - // Normalize so diagonal vectors don't multiply distance by √2 - return recommendedHeading.normalize(); } + + if (needX && needZ) { + // Both axes need correction — return a diagonal + return new Vector(dirX, 0, dirZ).normalize(); + } + + if (needX) { + return new Vector(dirX, 0, 0); + } + + // needZ is true + return new Vector(0, 0, dirZ); } /** diff --git a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java index 6b9a0d5..ed580ec 100644 --- a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java +++ b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java @@ -6,10 +6,10 @@ import org.jetbrains.annotations.NotNull; public class JumpValidator { - private static final double MAX_JUMP_DISTANCE = 4.5; - private static final double MAX_HORIZONTAL_DISTANCE = 4.1; - private static final double MAX_VERTICAL_UP = 1.25; - private static final double MAX_VERTICAL_DOWN = 3.0; + public static final double MAX_JUMP_DISTANCE = 4.5; + public static final double MAX_HORIZONTAL_DISTANCE = 4.1; + public static final double MAX_VERTICAL_UP = 1.25; + public static final double MAX_VERTICAL_DOWN = 3.0; private final double maxDistance; private final double maxHorizontal; @@ -122,43 +122,7 @@ public class JumpValidator { // Check if jump is possible: sqrt(dx² + dy² + dz²) <= maxDistance public boolean canJump(@NotNull Vector from, @NotNull Vector to) { - // Null safety check - if (from == null || to == null) { - return false; - } - - double dx = to.getX() - from.getX(); - double dy = to.getY() - from.getY(); - double dz = to.getZ() - from.getZ(); - - // Check if same location (no jump) - double horizontalDistance = Math.sqrt(dx * dx + dz * dz); - if (horizontalDistance < 0.01 && Math.abs(dy) < 0.01) { - return false; // Same location - } - - // Check horizontal distance limit - if (horizontalDistance > maxHorizontal) { - return false; - } - - // Check vertical limits - if (dy > maxVerticalUp || dy < -maxVerticalDown) { - return false; - } - - // For upward jumps, reduce max horizontal distance - // Player can't jump as far when jumping up - if (dy > 0) { - double adjustedMaxHorizontal = maxHorizontal - (dy * 0.5); // Reduce by 0.5 blocks per block up - if (horizontalDistance > adjustedMaxHorizontal) { - return false; - } - } - - // Check total distance - double totalDistance = Math.sqrt(dx * dx + dy * dy + dz * dz); - return totalDistance <= maxDistance; + return canJumpWithAdjustment(from, to, 0.0); } public double calculateDistance(@NotNull Location from, @NotNull Location to) { diff --git a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java index e0611ba..15d4e38 100644 --- a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java +++ b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java @@ -110,14 +110,23 @@ public class PlayerInteractionHandler { // Check if it's a schematic end block if (isSchematicEndBlock(block)) { handleSchematicEndBlock(block); + } else if (isOnSchematicBlock(block)) { + // Schematic block that is NOT the last — skip scoring + return; } - + // Check for scoring (compare by coordinates, not reference — Bukkit Block uses identity) if (historyContains(generator.state.history, block) && !player.hasScored(block)) { player.markScored(block); handleScore(); } } + + /** Check if block is any schematic block (not just the last one). */ + private boolean isOnSchematicBlock(@NotNull Block block) { + return generator.state.schematicBlocks != null + && historyContains(generator.state.schematicBlocks, block); + } private void handleSchematicEndBlock(@NotNull Block block) { // Remove schematic blocks and continue with normal generation @@ -131,8 +140,13 @@ public class PlayerInteractionHandler { } private boolean isSchematicEndBlock(@NotNull Block block) { - return generator.state.schematicBlocks != null - && historyContains(generator.state.schematicBlocks, block); + List schematicBlocks = generator.state.schematicBlocks; + if (schematicBlocks == null || schematicBlocks.isEmpty()) { + return false; + } + // Only the LAST block of the schematic triggers completion + Block last = schematicBlocks.get(schematicBlocks.size() - 1); + return isSameBlock(block, last); } /** Block directly under feet; {@code null} in air — scoring runs when landed. */