diff --git a/src/main/java/io/github/itzispyder/clickcrystals/scripting/components/conditionalcontexts/ConditionalBlockInFov.java b/src/main/java/io/github/itzispyder/clickcrystals/scripting/components/conditionalcontexts/ConditionalBlockInFov.java index 7fb09f32..ca157250 100644 --- a/src/main/java/io/github/itzispyder/clickcrystals/scripting/components/conditionalcontexts/ConditionalBlockInFov.java +++ b/src/main/java/io/github/itzispyder/clickcrystals/scripting/components/conditionalcontexts/ConditionalBlockInFov.java @@ -4,32 +4,77 @@ import io.github.itzispyder.clickcrystals.scripting.components.ConditionEvaluationContext; import io.github.itzispyder.clickcrystals.scripting.components.ConditionEvaluationResult; import io.github.itzispyder.clickcrystals.scripting.components.Conditional; -import io.github.itzispyder.clickcrystals.util.minecraft.EntityUtils; import io.github.itzispyder.clickcrystals.util.minecraft.PlayerUtils; import net.minecraft.block.BlockState; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; public class ConditionalBlockInFov implements Conditional { + private static final double DEFAULT_RANGE = 6.0; + private static final float[] YAW_OFFSETS = {0, -1, 1}; + private static final float[] PITCH_OFFSETS = {0, -1, 1}; + @Override public ConditionEvaluationResult evaluate(ConditionEvaluationContext ctx) { - Predicate filter = ctx.match(0, "any_block") ? state -> true : ScriptParser.parseBlockPredicate(ctx.get(0).toString()); - AtomicBoolean bl = new AtomicBoolean(false); - float fovDeg = ctx.get(1).toFloat(); - - EntityUtils.runOnNearestBlock(ctx.entity, 32, - (pos, state) -> filter.test(state) && validBlock(pos, fovDeg), - (pos, state) -> bl.set(true)); - return ctx.end(true, bl.get()); + if (!PlayerUtils.valid()) + return ctx.end(true, false); + + Predicate filter = resolveFilter(ctx); + float fovDeg = resolveFov(ctx); + + boolean found = scanCone(ctx, filter, fovDeg); + return ctx.end(true, found); + } + + private Predicate resolveFilter(ConditionEvaluationContext ctx) { + return ctx.match(0, "any_block") ? state -> true : ScriptParser.parseBlockPredicate(ctx.get(0).toString()); + } + + private float resolveFov(ConditionEvaluationContext ctx) { + float fov = ctx.get(1).toFloat(); + if (fov <= 0 || fov > 360) + return 90; + return fov; + } + + private boolean scanCone(ConditionEvaluationContext ctx, Predicate filter, float fovDeg) { + Vec3d eyes = PlayerUtils.getEyes(); + float yaw = PlayerUtils.player().getYaw(); + float pitch = PlayerUtils.player().getPitch(); + float step = fovDeg / 4; + + for (float yawMult : YAW_OFFSETS) { + for (float pitchMult : PITCH_OFFSETS) { + Vec3d dir = getDirection(yaw + yawMult * step, pitch + pitchMult * step); + Vec3d end = eyes.add(dir.multiply(DEFAULT_RANGE)); + + BlockHitResult hit = ctx.entity.getEntityWorld().raycast( + new RaycastContext(eyes, end, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, ctx.entity) + ); + + if (hit.getType() == HitResult.Type.BLOCK) { + BlockState state = ctx.entity.getEntityWorld().getBlockState(hit.getBlockPos()); + if (filter.test(state)) + return true; + } + } + } + return false; } - private boolean validBlock(BlockPos pos, float fovDeg) { - if (fovDeg != 360 && PlayerUtils.valid()) - if (!ConditionalEntityInFov.isPointInFov(PlayerUtils.getEyes(), PlayerUtils.getDir(), fovDeg, pos.toCenterPos())) - return false; - return pos.toCenterPos().distanceTo(PlayerUtils.getPos()) <= fovDeg; + private Vec3d getDirection(float yaw, float pitch) { + float yawRad = (float) Math.toRadians(yaw); + float pitchRad = (float) Math.toRadians(pitch); + float cosPitch = (float) Math.cos(pitchRad); + return new Vec3d( + -Math.sin(yawRad) * cosPitch, + -Math.sin(pitchRad), + Math.cos(yawRad) * cosPitch + ); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/itzispyder/clickcrystals/util/minecraft/PlayerUtils.java b/src/main/java/io/github/itzispyder/clickcrystals/util/minecraft/PlayerUtils.java index 08e6c4bb..faea710a 100644 --- a/src/main/java/io/github/itzispyder/clickcrystals/util/minecraft/PlayerUtils.java +++ b/src/main/java/io/github/itzispyder/clickcrystals/util/minecraft/PlayerUtils.java @@ -142,16 +142,33 @@ public static boolean isCollidingVertically() { } public static void boxIterator(World world, Box box, BiConsumer function) { - for (double x = box.minX; x <= box.maxX; x++) { - for (double y = box.minY; y <= box.maxY; y++) { - for (double z = box.minZ; z <= box.maxZ; z++) { - BlockPos pos = new BlockPos((int) Math.floor(x), (int) Math.floor(y), (int) Math.floor(z)); - BlockState state = world.getBlockState(pos); - - if (state == null || state.isAir()) { - continue; + Vec3d center = box.getCenter(); + int centerX = (int) Math.floor(center.x); + int centerY = (int) Math.floor(center.y); + int centerZ = (int) Math.floor(center.z); + double range = Math.max(Math.max(box.getXLength(), box.getYLength()), box.getZLength()) / 2; + int maxRadius = (int) Math.ceil(range); + + for (int radius = 0; radius <= maxRadius; radius++) { + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + if (Math.abs(x) != radius && Math.abs(y) != radius && Math.abs(z) != radius) { + continue; + } + + BlockPos pos = new BlockPos(centerX + x, centerY + y, centerZ + z); + if (!box.contains(pos.getX(), pos.getY(), pos.getZ())) { + continue; + } + + BlockState state = world.getBlockState(pos); + if (state == null || state.isAir()) { + continue; + } + + function.accept(pos, state); } - function.accept(pos, state); } } }