fix(generator): fix 6 generation bugs
- Fix JumpDirector diagonal drift - axis-aligned vectors per border side - Fix schematic end-block - only last block triggers completion, skip scoring - Fix JumpOffsetGenerator - clamp random offset to validator limits - Fix JumpCalculator retry height clamp to -2 - Fix JumpValidator duplication - Vector.canJump delegates to common impl - Remove rotateAroundY (offset already aligned with heading)
This commit is contained in:
parent
0b6409397b
commit
5b946c4222
4 changed files with 55 additions and 67 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.loki.loparkour.generator.jump.calculation;
|
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.model.GeneratorOption;
|
||||||
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.generator.jump.placement.BlockSelector;
|
import dev.loki.loparkour.generator.jump.placement.BlockSelector;
|
||||||
|
|
@ -100,7 +99,10 @@ public class JumpCalculator {
|
||||||
@NotNull
|
@NotNull
|
||||||
private Vector calculateJumpOffset(int distance, int height) {
|
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;
|
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);
|
Vector offset = generator.state.heading.clone().multiply(distance).setY(height);
|
||||||
if (offset.getX() == 0) {
|
if (offset.getX() == 0) {
|
||||||
|
|
@ -109,7 +111,6 @@ public class JumpCalculator {
|
||||||
offset.setZ(randomOffset);
|
offset.setZ(randomOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset.rotateAroundY(angleInY(generator.state.heading, Option.HEADING.getDirection()));
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +122,7 @@ public class JumpCalculator {
|
||||||
while (!validator.canJump(current.getLocation(), candidate.getLocation()) && attempts < 10) {
|
while (!validator.canJump(current.getLocation(), candidate.getLocation()) && attempts < 10) {
|
||||||
// Reduce distance and height to make jump easier
|
// Reduce distance and height to make jump easier
|
||||||
distance = Math.max(1, distance - 1);
|
distance = Math.max(1, distance - 1);
|
||||||
height = Math.max(-1, height - 1);
|
height = Math.max(-2, height - 1);
|
||||||
|
|
||||||
Vector offset = calculateJumpOffset(distance, height);
|
Vector offset = calculateJumpOffset(distance, height);
|
||||||
candidate = current.getLocation().add(offset).getBlock();
|
candidate = current.getLocation().add(offset).getBlock();
|
||||||
|
|
@ -151,10 +152,6 @@ public class JumpCalculator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
* Represents jump constraints for a specific block type.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -80,30 +80,43 @@ public class JumpDirector {
|
||||||
double tz = progress[2][0];
|
double tz = progress[2][0];
|
||||||
double borderMarginZ = progress[2][1];
|
double borderMarginZ = progress[2][1];
|
||||||
|
|
||||||
Vector recommendedHeading = new Vector(0, 0, 0);
|
// Determine per-axis corrections needed
|
||||||
// check border
|
boolean needX = false;
|
||||||
|
boolean needZ = false;
|
||||||
|
double dirX = 0;
|
||||||
|
double dirZ = 0;
|
||||||
|
|
||||||
if (tx < borderMarginX) {
|
if (tx < borderMarginX) {
|
||||||
// x should increase
|
needX = true;
|
||||||
recommendedHeading = new Vector(1, 0, 1);
|
dirX = 1;
|
||||||
} else if (tx > 1 - borderMarginX) {
|
} else if (tx > 1 - borderMarginX) {
|
||||||
// x should decrease
|
needX = true;
|
||||||
recommendedHeading = new Vector(-1, 0, -1);
|
dirX = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tz < borderMarginZ) {
|
if (tz < borderMarginZ) {
|
||||||
// z should increase
|
needZ = true;
|
||||||
recommendedHeading = new Vector(1, 0, 1);
|
dirZ = 1;
|
||||||
} else if (tz > 1 - borderMarginZ) {
|
} else if (tz > 1 - borderMarginZ) {
|
||||||
// z should decrease
|
needZ = true;
|
||||||
recommendedHeading = new Vector(-1, 0, -1);
|
dirZ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recommendedHeading.lengthSquared() == 0) {
|
if (!needX && !needZ) {
|
||||||
return current;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class JumpValidator {
|
public class JumpValidator {
|
||||||
|
|
||||||
private static final double MAX_JUMP_DISTANCE = 4.5;
|
public static final double MAX_JUMP_DISTANCE = 4.5;
|
||||||
private static final double MAX_HORIZONTAL_DISTANCE = 4.1;
|
public static final double MAX_HORIZONTAL_DISTANCE = 4.1;
|
||||||
private static final double MAX_VERTICAL_UP = 1.25;
|
public static final double MAX_VERTICAL_UP = 1.25;
|
||||||
private static final double MAX_VERTICAL_DOWN = 3.0;
|
public static final double MAX_VERTICAL_DOWN = 3.0;
|
||||||
|
|
||||||
private final double maxDistance;
|
private final double maxDistance;
|
||||||
private final double maxHorizontal;
|
private final double maxHorizontal;
|
||||||
|
|
@ -122,43 +122,7 @@ public class JumpValidator {
|
||||||
|
|
||||||
// Check if jump is possible: sqrt(dx² + dy² + dz²) <= maxDistance
|
// Check if jump is possible: sqrt(dx² + dy² + dz²) <= maxDistance
|
||||||
public boolean canJump(@NotNull Vector from, @NotNull Vector to) {
|
public boolean canJump(@NotNull Vector from, @NotNull Vector to) {
|
||||||
// Null safety check
|
return canJumpWithAdjustment(from, to, 0.0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double calculateDistance(@NotNull Location from, @NotNull Location to) {
|
public double calculateDistance(@NotNull Location from, @NotNull Location to) {
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,9 @@ public class PlayerInteractionHandler {
|
||||||
// Check if it's a schematic end block
|
// Check if it's a schematic end block
|
||||||
if (isSchematicEndBlock(block)) {
|
if (isSchematicEndBlock(block)) {
|
||||||
handleSchematicEndBlock(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)
|
// Check for scoring (compare by coordinates, not reference — Bukkit Block uses identity)
|
||||||
|
|
@ -119,6 +122,12 @@ public class PlayerInteractionHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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) {
|
private void handleSchematicEndBlock(@NotNull Block block) {
|
||||||
// Remove schematic blocks and continue with normal generation
|
// Remove schematic blocks and continue with normal generation
|
||||||
if (generator.state.schematicBlocks != null) {
|
if (generator.state.schematicBlocks != null) {
|
||||||
|
|
@ -131,8 +140,13 @@ public class PlayerInteractionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSchematicEndBlock(@NotNull Block block) {
|
private boolean isSchematicEndBlock(@NotNull Block block) {
|
||||||
return generator.state.schematicBlocks != null
|
List<Block> schematicBlocks = generator.state.schematicBlocks;
|
||||||
&& historyContains(generator.state.schematicBlocks, block);
|
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. */
|
/** Block directly under feet; {@code null} in air — scoring runs when landed. */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue