From 25ae75bfb6859585269d85f245f563938ff73bfa Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Tue, 19 Mar 2019 16:29:48 +0100 Subject: [PATCH] Refactored build modes for easier expansion. Allowed buildmodes to intersect with existing blocks 1 block deep. --- .../buildmode/BuildModes.java | 27 +++ .../effortlessbuilding/buildmode/Floor.java | 94 ++++---- .../effortlessbuilding/buildmode/Line.java | 208 ++++++++---------- .../effortlessbuilding/buildmode/Wall.java | 136 ++++++------ .../render/BlockPreviewRenderer.java | 2 +- 5 files changed, 249 insertions(+), 218 deletions(-) diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index f45d93c..6f3b3f9 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -192,4 +192,31 @@ public class BuildModes { Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; return currentlyBreaking.get(player) != null; } + + //Find coordinates on a line bound by a plane + protected static Vec3d findXBound(double x, Vec3d start, Vec3d look) { + //then y and z are + double y = (x - start.x) / look.x * look.y + start.y; + double z = (x - start.x) / look.x * look.z + start.z; + + return new Vec3d(x, y, z); + } + + protected static Vec3d findYBound(double y, Vec3d start, Vec3d look) { + //then x and z are + double x = (y - start.y) / look.y * look.x + start.x; + double z = (y - start.y) / look.y * look.z + start.z; + + return new Vec3d(x, y, z); + } + + protected static Vec3d findZBound(double z, Vec3d start, Vec3d look) { + //then x and y are + double x = (z - start.z) / look.z * look.x + start.x; + double y = (z - start.z) / look.z * look.y + start.y; + + return new Vec3d(x, y, z); + } + + } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java index d55b02d..203ffc0 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java @@ -5,6 +5,7 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.helper.ReachHelper; import java.util.*; @@ -18,6 +19,33 @@ public class Floor implements IBuildMode { Dictionary sideHitTable = new Hashtable<>(); Dictionary hitVecTable = new Hashtable<>(); + class Criteria { + Vec3d planeBound; + double distToPlayerSq; + + Criteria(Vec3d planeBound, Vec3d start) { + this.planeBound = planeBound; + this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared(); + } + + //check if its not behind the player and its not too close and not too far + //also check if raytrace from player to block does not intersect blocks + public boolean isValid(Vec3d start, Vec3d look, int reach, EntityPlayer player, boolean skipRaytrace) { + + boolean intersects = false; + if (!skipRaytrace) { + //collision within a 1 block radius to selected is fine + RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, planeBound, false, true, false); + intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && + planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; + } + + return planeBound.subtract(start).dotProduct(look) > 0 && + distToPlayerSq > 4 && distToPlayerSq < reach * reach && + !intersects; + } + } + @Override public void initialize(EntityPlayer player) { rightClickClientTable.put(player.getUniqueID(), 0); @@ -69,47 +97,10 @@ public class Floor implements IBuildMode { if (blockPos != null) list.add(blockPos); } else { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); + BlockPos secondPos = findFloor(player, firstPos, skipRaytrace); + if (secondPos == null) return list; - //try on z axis - double y = firstPos.getY(); - - //then x and z are - double x = (y - start.y) / look.y * look.x + start.x; - double z = (y - start.y) / look.y * look.z + start.z; - - Vec3d yBound = new Vec3d(x, y, z); - - //distance to player - double yDistSquared = yBound.subtract(start).lengthSquared(); - - - int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach - - //check if its not behind the player and its not too close and not too far - boolean yValid = yBound.subtract(start).dotProduct(look) > 0 && - yDistSquared > 4 && yDistSquared < reach * reach; - - //select the one that is closest to the player and is valid - Vec3d selected = null; - if (yValid) selected = yBound; - - if (selected == null) return list; - - //check if it doesnt go through blocks - //TODO collision within a 1 block radius to selected is fine - if (!skipRaytrace) { - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, selected, false, true, false); - if (rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK) { - //return empty list - return list; - } - } - - BlockPos secondPos = new BlockPos(selected); - - //Add whole wall + //Add whole floor //Limit amount of blocks you can place per row int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); @@ -135,6 +126,29 @@ public class Floor implements IBuildMode { return list; } + public BlockPos findFloor(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + Vec3d look = player.getLookVec(); + Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); + + List criteriaList = new ArrayList<>(3); + + //Y + Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look); + criteriaList.add(new Criteria(yBound, start)); + + //Remove invalid criteria + int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach + criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); + + //If none are valid, return empty list of blocks + if (criteriaList.isEmpty()) return null; + + //Then only 1 can be valid, return that one + Criteria selected = criteriaList.get(0); + + return new BlockPos(selected.planeBound); + } + @Override public EnumFacing getSideHit(EntityPlayer player) { return sideHitTable.get(player.getUniqueID()); diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java index e7be3e1..03a8f13 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java @@ -18,6 +18,57 @@ public class Line implements IBuildMode { Dictionary sideHitTable = new Hashtable<>(); Dictionary hitVecTable = new Hashtable<>(); + class Criteria { + Vec3d planeBound; + Vec3d lineBound; + double distToLineSq; + double distToPlayerSq; + + Criteria(Vec3d planeBound, BlockPos firstPos, Vec3d start) { + this.planeBound = planeBound; + this.lineBound = toLongestLine(this.planeBound, firstPos); + this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSquared(); + this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared(); + } + + //Make it from a plane into a line + //Select the axis that is longest + private Vec3d toLongestLine(Vec3d boundVec, BlockPos firstPos) { + BlockPos bound = new BlockPos(boundVec); + + BlockPos firstToSecond = bound.subtract(firstPos); + firstToSecond = new BlockPos(Math.abs(firstToSecond.getX()), Math.abs(firstToSecond.getY()), Math.abs(firstToSecond.getZ())); + int longest = Math.max(firstToSecond.getX(), Math.max(firstToSecond.getY(), firstToSecond.getZ())); + if (longest == firstToSecond.getX()) { + return new Vec3d(bound.getX(), firstPos.getY(), firstPos.getZ()); + } + if (longest == firstToSecond.getY()) { + return new Vec3d(firstPos.getX(), bound.getY(), firstPos.getZ()); + } + if (longest == firstToSecond.getZ()) { + return new Vec3d(firstPos.getX(), firstPos.getY(), bound.getZ()); + } + return null; + } + + //check if its not behind the player and its not too close and not too far + //also check if raytrace from player to block does not intersect blocks + public boolean isValid(Vec3d start, Vec3d look, int reach, EntityPlayer player, boolean skipRaytrace) { + + boolean intersects = false; + if (!skipRaytrace) { + //collision within a 1 block radius to selected is fine + RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, lineBound, false, true, false); + intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && + planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; + } + + return planeBound.subtract(start).dotProduct(look) > 0 && + distToPlayerSq > 4 && distToPlayerSq < reach * reach && + !intersects; + } + } + @Override public void initialize(EntityPlayer player) { rightClickClientTable.put(player.getUniqueID(), 0); @@ -69,107 +120,13 @@ public class Line implements IBuildMode { if (blockPos != null) list.add(blockPos); } else { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - - - //try on x axis - double x = firstPos.getX(); - - //then y and z are - double y = (x - start.x) / look.x * look.y + start.y; - double z = (x - start.x) / look.x * look.z + start.z; - - Vec3d xBound = new Vec3d(x, y, z); - Vec3d xBoundLine = toLongestLine(xBound, firstPos); - double xDistToLine = xBoundLine.subtract(xBound).lengthSquared(); - - //distance to player - double xDistSquared = xBound.subtract(start).lengthSquared(); - - - - //try on y axis - y = firstPos.getY(); - - //then x and z are - x = (y - start.y) / look.y * look.x + start.x; - z = (y - start.y) / look.y * look.z + start.z; - - Vec3d yBound = new Vec3d(x, y, z); - Vec3d yBoundLine = toLongestLine(yBound, firstPos); - double yDistToLine = yBoundLine.subtract(yBound).lengthSquared(); - - //distance to player - double yDistSquared = yBound.subtract(start).lengthSquared(); - - - - //try on z axis - z = firstPos.getZ(); - - //then x and y are - x = (z - start.z) / look.z * look.x + start.x; - y = (z - start.z) / look.z * look.y + start.y; - - Vec3d zBound = new Vec3d(x, y, z); - Vec3d zBoundLine = toLongestLine(zBound, firstPos); - double zDistToLine = zBoundLine.subtract(zBound).lengthSquared(); - - //distance to player - double zDistSquared = zBound.subtract(start).lengthSquared(); - - - - int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach - - //check if its not behind the player and its not too close and not too far - boolean xValid = xBound.subtract(start).dotProduct(look) > 0 && - xDistSquared > 4 && xDistSquared < reach * reach; - - boolean yValid = yBound.subtract(start).dotProduct(look) > 0 && - yDistSquared > 4 && yDistSquared < reach * reach; - - boolean zValid = zBound.subtract(start).dotProduct(look) > 0 && - zDistSquared > 4 && zDistSquared < reach * reach; - - //select the one that is closest (from wall position to its line counterpart) and is valid - //TODO: if multiple are very close, choose closest to player - Vec3d selected = null; - double selectedDistToLine = 0; - - if (xValid) { - selected = xBoundLine; - selectedDistToLine = xDistToLine; - } else if (yValid) { - selected = yBoundLine; - selectedDistToLine = yDistToLine; - } else if (zValid) { - selected = zBoundLine; - selectedDistToLine = yDistToLine; - } - - if (yValid && yDistToLine < selectedDistToLine) selected = yBoundLine; - if (zValid && zDistToLine < selectedDistToLine) selected = zBoundLine; - - if (selected == null) return list; - - //check if it doesnt go through blocks - if (!skipRaytrace) { - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, selected, false, true, false); - if (rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK) { - //return empty list - return list; - } - } - - BlockPos secondPos = new BlockPos(selected); + BlockPos secondPos = findLine(player, firstPos, skipRaytrace); + if (secondPos == null) return list; + //Limit amount of blocks we can place int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); //Add whole line - int x1 = firstPos.getX(), x2 = secondPos.getX(); int y1 = firstPos.getY(), y2 = secondPos.getY(); int z1 = firstPos.getZ(), z2 = secondPos.getZ(); @@ -190,26 +147,51 @@ public class Line implements IBuildMode { return list; } - //Make it into a line - //Select the axis that is longest - private Vec3d toLongestLine(Vec3d boundVec, BlockPos firstPos) { - BlockPos bound = new BlockPos(boundVec); + public BlockPos findLine(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + Vec3d look = player.getLookVec(); + Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look); + criteriaList.add(new Criteria(xBound, firstPos, start)); + + //Y + Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look); + criteriaList.add(new Criteria(yBound, firstPos, start)); + + //Z + Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look); + criteriaList.add(new Criteria(zBound, firstPos, start)); + + //Remove invalid criteria + int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach + criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); + + //If none are valid, return empty list of blocks + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + Criteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest (from wall position to its line counterpart) + for (int i = 1; i < criteriaList.size(); i++) { + Criteria criteria = criteriaList.get(i); + if (criteria.distToLineSq < selected.distToLineSq) + selected = criteria; + } + + //TODO: if multiple are very close, choose closest to player - BlockPos firstToSecond = bound.subtract(firstPos); - firstToSecond = new BlockPos(Math.abs(firstToSecond.getX()), Math.abs(firstToSecond.getY()), Math.abs(firstToSecond.getZ())); - int longest = Math.max(firstToSecond.getX(), Math.max(firstToSecond.getY(), firstToSecond.getZ())); - if (longest == firstToSecond.getX()) { - return new Vec3d(bound.getX(), firstPos.getY(), firstPos.getZ()); } - if (longest == firstToSecond.getY()) { - return new Vec3d(firstPos.getX(), bound.getY(), firstPos.getZ()); - } - if (longest == firstToSecond.getZ()) { - return new Vec3d(firstPos.getX(), firstPos.getY(), bound.getZ()); - } - return null; + + return new BlockPos(selected.lineBound); } + @Override public EnumFacing getSideHit(EntityPlayer player) { return sideHitTable.get(player.getUniqueID()); diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java index 157622e..807fda9 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java @@ -18,6 +18,35 @@ public class Wall implements IBuildMode { Dictionary sideHitTable = new Hashtable<>(); Dictionary hitVecTable = new Hashtable<>(); + class Criteria { + Vec3d planeBound; + double distToPlayerSq; + double angle; + + Criteria(Vec3d planeBound, BlockPos firstPos, Vec3d start, Vec3d look) { + this.planeBound = planeBound; + this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared(); + this.angle = this.planeBound.subtract(new Vec3d(firstPos)).normalize().dotProduct(look); + } + + //check if its not behind the player and its not too close and not too far + //also check if raytrace from player to block does not intersect blocks + public boolean isValid(Vec3d start, Vec3d look, int reach, EntityPlayer player, boolean skipRaytrace) { + + boolean intersects = false; + if (!skipRaytrace) { + //collision within a 1 block radius to selected is fine + RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, planeBound, false, true, false); + intersects = rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && + planeBound.subtract(rayTraceResult.hitVec).lengthSquared() > 4; + } + + return planeBound.subtract(start).dotProduct(look) > 0 && + distToPlayerSq > 4 && distToPlayerSq < reach * reach && + !intersects; + } + } + @Override public void initialize(EntityPlayer player) { rightClickClientTable.put(player.getUniqueID(), 0); @@ -69,72 +98,11 @@ public class Wall implements IBuildMode { if (blockPos != null) list.add(blockPos); } else { - Vec3d look = player.getLookVec(); - Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); - - //try on x axis - double x = firstPos.getX(); - - //then y and z are - double y = (x - start.x) / look.x * look.y + start.y; - double z = (x - start.x) / look.x * look.z + start.z; - - Vec3d xBound = new Vec3d(x, y, z); - - //distance to player - double xDistSquared = xBound.subtract(start).lengthSquared(); - - //angle to look - //double xAngle = xBound.subtract(new Vec3d(firstPos)).normalize().dotProduct(look); - - //try on z axis - z = firstPos.getZ(); - - //then x and y are - x = (z - start.z) / look.z * look.x + start.x; - y = (z - start.z) / look.z * look.y + start.y; - - Vec3d zBound = new Vec3d(x, y, z); - - //distance to player - double zDistSquared = zBound.subtract(start).lengthSquared(); - - //angle to look - //double zAngle = zBound.subtract(new Vec3d(firstPos)).normalize().dotProduct(look); - - int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach - - //check if its not behind the player and its not too close and not too far - boolean xValid = xBound.subtract(start).dotProduct(look) > 0 && - xDistSquared > 4 && xDistSquared < reach * reach; - - boolean zValid = zBound.subtract(start).dotProduct(look) > 0 && - zDistSquared > 4 && zDistSquared < reach * reach; - - //select the one that is closest and is valid - Vec3d selected = null; - if (xValid) - selected = xBound; - else if (zValid) - selected = zBound; - if (zValid && zDistSquared < xDistSquared/*Math.abs(zAngle) < Math.abs(xAngle)*/) selected = zBound; - - if (selected == null) return list; - - //check if it doesnt go through blocks - //TODO collision within a 1 block radius to selected is fine - if (!skipRaytrace) { - RayTraceResult rayTraceResult = player.world.rayTraceBlocks(start, selected, false, true, false); - if (rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK) { - //return empty list - return list; - } - } - - BlockPos secondPos = new BlockPos(selected); + BlockPos secondPos = findWall(player, firstPos, skipRaytrace); + if (secondPos == null) return list; //Add whole wall - //Limit amount of blocks you can place per row + //Limit amount of blocks we can place per row int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); @@ -168,6 +136,46 @@ public class Wall implements IBuildMode { return list; } + public BlockPos findWall(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + Vec3d look = player.getLookVec(); + Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look); + criteriaList.add(new Criteria(xBound, firstPos, start, look)); + + //Z + Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look); + criteriaList.add(new Criteria(zBound, firstPos, start, look)); + + //Remove invalid criteria + int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach + criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); + + //If none are valid, return empty list of blocks + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + Criteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest + for (int i = 1; i < criteriaList.size(); i++) { + Criteria criteria = criteriaList.get(i); + if (criteria.distToPlayerSq < selected.distToPlayerSq) + selected = criteria; + } + + //TODO: limit angle + //Math.abs(zAngle) < Math.abs(xAngle) + } + + return new BlockPos(selected.planeBound); + } + @Override public EnumFacing getSideHit(EntityPlayer player) { return sideHitTable.get(player.getUniqueID()); diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java index 36deedf..465f82c 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java @@ -300,7 +300,7 @@ public class BlockPreviewRenderer { //Check if can place //If check is turned off, check if blockstate is the same (for dissolve effect) - if ((!checkCanPlace /*&& player.world.getBlockState(blockPos) == blockState*/) || //TODO enable + if ((!checkCanPlace /*&& player.world.getBlockState(blockPos) == blockState*/) || //TODO enable (breaks breaking shader) SurvivalHelper.canPlace(player.world, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), EnumFacing.UP)) { ShaderHandler.useShader(ShaderHandler.dissolve, generateShaderCallback(dissolve,