diff --git a/build.gradle b/build.gradle index 62b15e0..7b92238 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge' //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -version = "1.12.2-2.15" +version = "1.12.2-2.16" group = "nl.requios.effortlessbuilding" // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "effortlessbuilding" diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index f2dc819..11a7182 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -39,7 +39,7 @@ public class EffortlessBuilding { public static final String MODID = "effortlessbuilding"; public static final String NAME = "Effortless Building"; - public static final String VERSION = "1.12.2-2.15"; + public static final String VERSION = "1.12.2-2.16"; @Mod.Instance(EffortlessBuilding.MODID) public static EffortlessBuilding instance; diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java new file mode 100644 index 0000000..ac4da0d --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java @@ -0,0 +1,40 @@ +package nl.requios.effortlessbuilding.buildmode; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; +import java.util.UUID; + +public abstract class BaseBuildMode implements IBuildMode { + //In singleplayer client and server variables are shared + //Split everything that needs separate values and may not be called twice in one click + protected Dictionary rightClickClientTable = new Hashtable<>(); + protected Dictionary rightClickServerTable = new Hashtable<>(); + protected Dictionary firstPosTable = new Hashtable<>(); + protected Dictionary sideHitTable = new Hashtable<>(); + protected Dictionary hitVecTable = new Hashtable<>(); + + @Override + public void initialize(EntityPlayer player) { + rightClickClientTable.put(player.getUniqueID(), 0); + rightClickServerTable.put(player.getUniqueID(), 0); + firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); + sideHitTable.put(player.getUniqueID(), EnumFacing.UP); + hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); + } + + @Override + public EnumFacing getSideHit(EntityPlayer player) { + return sideHitTable.get(player.getUniqueID()); + } + + @Override + public Vec3d getHitVec(EntityPlayer player) { + return hitVecTable.get(player.getUniqueID()); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index 2c557de..ba85003 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -4,11 +4,14 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; 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.buildmode.ModeOptions.ActionEnum; +import nl.requios.effortlessbuilding.buildmode.buildmodes.*; +import nl.requios.effortlessbuilding.buildmode.buildmodes.Circle; +import nl.requios.effortlessbuilding.buildmode.buildmodes.Cylinder; +import nl.requios.effortlessbuilding.buildmode.buildmodes.Sphere; import nl.requios.effortlessbuilding.buildmodifier.*; -import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.network.BlockBrokenMessage; @@ -19,6 +22,8 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; +import static nl.requios.effortlessbuilding.buildmode.ModeOptions.*; + public class BuildModes { //Static variables are shared between client and server in singleplayer @@ -27,21 +32,24 @@ public class BuildModes { public static Dictionary currentlyBreakingServer = new Hashtable<>(); public enum BuildModeEnum { - NORMAL("effortlessbuilding.mode.normal", new Normal(), new ActionEnum[]{}), - NORMAL_PLUS("effortlessbuilding.mode.normal_plus", new NormalPlus(), new ActionEnum[]{ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED}), - LINE("effortlessbuilding.mode.line", new Line(), new ActionEnum[]{/*ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5*/}), - WALL("effortlessbuilding.mode.wall", new Wall(), new ActionEnum[]{ActionEnum.FULL, ActionEnum.HOLLOW}), - FLOOR("effortlessbuilding.mode.floor", new Floor(), new ActionEnum[]{ActionEnum.FULL, ActionEnum.HOLLOW}), - DIAGONAL_LINE("effortlessbuilding.mode.diagonal_line", new DiagonalLine(), new ActionEnum[]{/*ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5*/}), - DIAGONAL_WALL("effortlessbuilding.mode.diagonal_wall", new DiagonalWall(), new ActionEnum[]{/*ActionEnum.FULL, ActionEnum.HOLLOW*/}), - SLOPE_FLOOR("effortlessbuilding.mode.slope_floor", new SlopeFloor(), new ActionEnum[]{ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE}), - CUBE("effortlessbuilding.mode.cube", new Cube(), new ActionEnum[]{ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON}); + NORMAL("effortlessbuilding.mode.normal", new Normal()), + NORMAL_PLUS("effortlessbuilding.mode.normal_plus", new NormalPlus(), OptionEnum.BUILD_SPEED), + LINE("effortlessbuilding.mode.line", new Line() /*, OptionEnum.THICKNESS*/), + WALL("effortlessbuilding.mode.wall", new Wall(), OptionEnum.FILL), + FLOOR("effortlessbuilding.mode.floor", new Floor(), OptionEnum.FILL), + DIAGONAL_LINE("effortlessbuilding.mode.diagonal_line", new DiagonalLine() /*, OptionEnum.THICKNESS*/), + DIAGONAL_WALL("effortlessbuilding.mode.diagonal_wall", new DiagonalWall() /*, OptionEnum.FILL*/), + SLOPE_FLOOR("effortlessbuilding.mode.slope_floor", new SlopeFloor(), OptionEnum.RAISED_EDGE), + CIRCLE("effortlessbuilding.mode.circle", new Circle(), OptionEnum.CIRCLE_START, OptionEnum.FILL), + CYLINDER("effortlessbuilding.mode.cylinder", new Cylinder(), OptionEnum.CIRCLE_START, OptionEnum.FILL), + SPHERE("effortlessbuilding.mode.sphere", new Sphere(), OptionEnum.CIRCLE_START, OptionEnum.FILL), + CUBE("effortlessbuilding.mode.cube", new Cube(), OptionEnum.CUBE_FILL); public String name; public IBuildMode instance; - public ActionEnum[] options; + public OptionEnum[] options; - BuildModeEnum(String name, IBuildMode instance, ActionEnum[] options) { + BuildModeEnum(String name, IBuildMode instance, OptionEnum... options) { this.name = name; this.instance = instance; this.options = options; @@ -220,4 +228,18 @@ public class BuildModes { return new Vec3d(x, y, z); } + public static boolean isCriteriaValid(Vec3d start, Vec3d look, int reach, EntityPlayer player, boolean skipRaytrace, Vec3d lineBound, Vec3d planeBound, double distToPlayerSq) { + 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 > 2 && distToPlayerSq < reach * reach && + !intersects; + } + } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java deleted file mode 100644 index 12258bd..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Cube.java +++ /dev/null @@ -1,208 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class Cube implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else if (rightClickNr == 2) { - //Second click, find other floor point - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = Floor.findFloor(player, firstPos, skipRaytrace); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place cube with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole floor - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y = firstPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON) { - //Hollow floor - Line.addXLineBlocks(list, x1, x2, y, z1); - Line.addXLineBlocks(list, x1, x2, y, z2); - Line.addZLineBlocks(list, z1, z2, x1, y); - Line.addZLineBlocks(list, z1, z2, x2, y); - } else { - //Filled floor - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - list.add(new BlockPos(l, y, n)); - } - } - } - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add whole cube - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = thirdPos.getX(); - int y1 = firstPos.getY(), y2 = thirdPos.getY(); - int z1 = firstPos.getZ(), z2 = thirdPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - switch (ModeOptions.getCubeFill()) { - case CUBE_FULL: - addCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - case CUBE_HOLLOW: - addHollowCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - case CUBE_SKELETON: - addSkeletonCubeBlocks(list, x1, x2, y1, y2, z1, z2); - break; - } - - } - - return list; - } - - public static void addCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(l, m, n)); - } - } - } - } - - public static void addHollowCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - Wall.addXWallBlocks(list, x1, y1, y2, z1, z2); - Wall.addXWallBlocks(list, x2, y1, y2, z1, z2); - - Wall.addZWallBlocks(list, x1, x2, y1, y2, z1); - Wall.addZWallBlocks(list, x1, x2, y1, y2, z2); - - Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); - Floor.addFloorBlocks(list, x1, x2, y2, z1, z2); - } - - public static void addSkeletonCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { - Line.addXLineBlocks(list, x1, x2, y1, z1); - Line.addXLineBlocks(list, x1, x2, y1, z2); - Line.addXLineBlocks(list, x1, x2, y2, z1); - Line.addXLineBlocks(list, x1, x2, y2, z2); - - Line.addYLineBlocks(list, y1, y2, x1, z1); - Line.addYLineBlocks(list, y1, y2, x1, z2); - Line.addYLineBlocks(list, y1, y2, x2, z1); - Line.addYLineBlocks(list, y1, y2, x2, z2); - - Line.addZLineBlocks(list, z1, z2, x1, y1); - Line.addZLineBlocks(list, z1, z2, x1, y2); - Line.addZLineBlocks(list, z1, z2, x2, y1); - Line.addZLineBlocks(list, z1, z2, x2, y2); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } - -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java deleted file mode 100644 index 7ba351c..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalWall.java +++ /dev/null @@ -1,138 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class DiagonalWall implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else if (rightClickNr == 2) { - //Second click, find other floor point - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place diagonal wall with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - if (secondPos == null) return list; - - //Add diagonal line - list.addAll(DiagonalLine.getDiagonalLineBlocks(player, firstPos, secondPos, 1)); - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add diagonal wall - list.addAll(getDiagonalWallBlocks(player, firstPos, secondPos, thirdPos)); - } - - return list; - } - - //Add diagonal wall from first to second - public static List getDiagonalWallBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, BlockPos thirdPos) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - //Get diagonal line blocks - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, secondPos, 1); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getY(), thirdPos.getY()); - int highest = Math.max(firstPos.getY(), thirdPos.getY()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on y axis - for (int y = lowest; y <= highest; y++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); - } - } - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java deleted file mode 100644 index 92aabbb..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Floor.java +++ /dev/null @@ -1,183 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -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.helper.ReachHelper; - -import java.util.*; - -public class Floor implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - static 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 > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findFloor(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole floor - list.addAll(getFloorBlocks(player, firstPos, secondPos)); - } - - return list; - } - - public static 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); - } - - public static List getFloorBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { - List list = new ArrayList<>(); - - //Limit amount of blocks you can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y = firstPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addFloorBlocks(list, x1, x2, y, z1, z2); - else - addHollowFloorBlocks(list, x1, x2, y, z1, z2); - - return list; - } - - public static void addFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { - - for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { - - for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { - - list.add(new BlockPos(l, y, n)); - } - } - } - - public static void addHollowFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { - Line.addXLineBlocks(list, x1, x2, y, z1); - Line.addXLineBlocks(list, x1, x2, y, z2); - Line.addZLineBlocks(list, z1, z2, x1, y); - Line.addZLineBlocks(list, z1, z2, x2, y); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java index e82ea70..81dec27 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java @@ -30,7 +30,10 @@ public class ModeOptions { THICKNESS_1("effortlessbuilding.action.thickness_1"), THICKNESS_3("effortlessbuilding.action.thickness_3"), - THICKNESS_5("effortlessbuilding.action.thickness_5"); + THICKNESS_5("effortlessbuilding.action.thickness_5"), + + CIRCLE_START_CORNER("effortlessbuilding.action.start_corner"), + CIRCLE_START_CENTER("effortlessbuilding.action.start_center"); public String name; @@ -39,11 +42,48 @@ public class ModeOptions { } } + public enum OptionEnum { + BUILD_SPEED("effortlessbuilding.action.build_speed", ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED), + FILL("effortlessbuilding.action.filling", ActionEnum.FULL, ActionEnum.HOLLOW), + CUBE_FILL("effortlessbuilding.action.filling", ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON), + RAISED_EDGE("effortlessbuilding.action.raised_edge", ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE), + LINE_THICKNESS("effortlessbuilding.action.thickness", ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5), + CIRCLE_START("effortlessbuilding.action.circle_start", ActionEnum.CIRCLE_START_CORNER, ActionEnum.CIRCLE_START_CENTER); + + public String name; + public ActionEnum[] actions; + + OptionEnum(String name, ActionEnum... actions){ + this.name = name; + this.actions = actions; + } + } + private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED; private static ActionEnum fill = ActionEnum.FULL; private static ActionEnum cubeFill = ActionEnum.CUBE_FULL; private static ActionEnum raisedEdge = ActionEnum.SHORT_EDGE; private static ActionEnum lineThickness = ActionEnum.THICKNESS_1; + private static ActionEnum circleStart = ActionEnum.CIRCLE_START_CORNER; + + public static ActionEnum getOptionSetting(OptionEnum option) { + switch (option) { + case BUILD_SPEED: + return getBuildSpeed(); + case FILL: + return getFill(); + case CUBE_FILL: + return getCubeFill(); + case RAISED_EDGE: + return getRaisedEdge(); + case LINE_THICKNESS: + return getLineThickness(); + case CIRCLE_START: + return getCircleStart(); + default: + return null; + } + } public static ActionEnum getBuildSpeed() { return buildSpeed; @@ -65,6 +105,10 @@ public class ModeOptions { return lineThickness; } + public static ActionEnum getCircleStart() { + return circleStart; + } + //Called on both client and server public static void performAction(EntityPlayer player, ActionEnum action) { if (action == null) return; @@ -123,6 +167,12 @@ public class ModeOptions { case THICKNESS_5: lineThickness = ActionEnum.THICKNESS_5; break; + case CIRCLE_START_CENTER: + circleStart = ActionEnum.CIRCLE_START_CENTER; + break; + case CIRCLE_START_CORNER: + circleStart = ActionEnum.CIRCLE_START_CORNER; + break; } if (player.world.isRemote && action != ActionEnum.REPLACE && action != ActionEnum.OPEN_MODIFIER_SETTINGS) { diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java deleted file mode 100644 index 35fa01a..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/SlopeFloor.java +++ /dev/null @@ -1,178 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import nl.requios.effortlessbuilding.helper.ReachHelper; - -import java.util.*; - -public class SlopeFloor implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else if (rightClickNr == 2) { - //Second click, find other floor point - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - - if (secondPos == null) { - rightClickTable.put(player.getUniqueID(), 1); - return list; - } - - secondPosTable.put(player.getUniqueID(), secondPos); - - } else { - //Third click, place slope floor with height - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else if (rightClickNr == 1) { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - BlockPos secondPos = Floor.findFloor(player, firstPos, true); - if (secondPos == null) return list; - - //Add whole floor - list.addAll(Floor.getFloorBlocks(player, firstPos, secondPos)); - - } else { - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - - BlockPos thirdPos = DiagonalLine.findHeight(player, secondPos, skipRaytrace); - if (thirdPos == null) return list; - - //Add slope floor blocks - list.addAll(getSlopeFloorBlocks(player, firstPos, secondPos, thirdPos)); - } - - return list; - } - - //Add slope floor from first to second - public static List getSlopeFloorBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, BlockPos thirdPos) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - //Determine whether to use x or z axis to slope up - boolean onXAxis = true; - - int xLength = Math.abs(secondPos.getX() - firstPos.getX()); - int zLength = Math.abs(secondPos.getZ() - firstPos.getZ()); - - if (ModeOptions.getRaisedEdge() == ModeOptions.ActionEnum.SHORT_EDGE) { - //Slope along short edge - if (zLength > xLength) onXAxis = false; - } else { - //Slope along long edge - if (zLength <= xLength) onXAxis = false; - } - - if (onXAxis) { - //Along X goes up - - //Get diagonal line blocks - BlockPos linePoint = new BlockPos(secondPos.getX(), thirdPos.getY(), firstPos.getZ()); - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, linePoint, 1f); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getZ(), secondPos.getZ()); - int highest = Math.max(firstPos.getZ(), secondPos.getZ()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on x axis - for (int z = lowest; z <= highest; z++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(blockPos.getX(), blockPos.getY(), z)); - } - } - - } else { - //Along Z goes up - - //Get diagonal line blocks - BlockPos linePoint = new BlockPos(firstPos.getX(), thirdPos.getY(), secondPos.getZ()); - List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, firstPos, linePoint, 1f); - - //Limit amount of blocks we can place - int lowest = Math.min(firstPos.getX(), secondPos.getX()); - int highest = Math.max(firstPos.getX(), secondPos.getX()); - - if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; - - //Copy diagonal line on x axis - for (int x = lowest; x <= highest; x++) { - for (BlockPos blockPos : diagonalLineBlocks) { - list.add(new BlockPos(x, blockPos.getY(), blockPos.getZ())); - } - } - } - - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java similarity index 61% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java index 734d251..a8f9ba5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/DiagonalLine.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java @@ -3,21 +3,15 @@ package nl.requios.effortlessbuilding.buildmode; import net.minecraft.entity.player.EntityPlayer; 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.buildmode.buildmodes.DiagonalLine; +import nl.requios.effortlessbuilding.buildmode.buildmodes.Floor; import nl.requios.effortlessbuilding.helper.ReachHelper; import java.util.*; -public class DiagonalLine implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary secondPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); +public abstract class ThreeClicksBuildMode extends BaseBuildMode { + protected Dictionary secondPosTable = new Hashtable<>(); static class HeightCriteria { Vec3d planeBound; @@ -42,27 +36,14 @@ public class DiagonalLine implements IBuildMode { //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 > 2 && distToPlayerSq < reach * reach && - !intersects; + return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq); } } - + @Override public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); + super.initialize(player); + secondPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); } @Override @@ -89,7 +70,7 @@ public class DiagonalLine implements IBuildMode { } else if (rightClickNr == 2) { //Second click, find other floor point BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = Floor.findFloor(player, firstPos, true); + BlockPos secondPos = findSecondPos(player, firstPos, true); if (secondPos == null) { rightClickTable.put(player.getUniqueID(), 1); @@ -99,7 +80,7 @@ public class DiagonalLine implements IBuildMode { secondPosTable.put(player.getUniqueID(), secondPos); } else { - //Third click, place diagonal line with height + //Third click, place diagonal wall with height list = findCoordinates(player, blockPos, skipRaytrace); rightClickTable.put(player.getUniqueID(), 0); } @@ -119,26 +100,75 @@ public class DiagonalLine implements IBuildMode { } else if (rightClickNr == 1) { BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - BlockPos secondPos = Floor.findFloor(player, firstPos, true); + BlockPos secondPos = findSecondPos(player, firstPos, true); if (secondPos == null) return list; + //Limit amount of blocks you can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + //Add diagonal line from first to second - list.addAll(getDiagonalLineBlocks(player , firstPos, secondPos, 10)); + list.addAll(getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)); } else { BlockPos firstPos = firstPosTable.get(player.getUniqueID()); BlockPos secondPos = secondPosTable.get(player.getUniqueID()); - BlockPos thirdPos = findHeight(player, secondPos, skipRaytrace); + BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, skipRaytrace); if (thirdPos == null) return list; + //Limit amount of blocks you can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(), x3 = thirdPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(), y3 = thirdPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(), z3 = thirdPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + + if (x3 - x1 >= axisLimit) x3 = x1 + axisLimit - 1; + if (x1 - x3 >= axisLimit) x3 = x1 - axisLimit + 1; + if (y3 - y1 >= axisLimit) y3 = y1 + axisLimit - 1; + if (y1 - y3 >= axisLimit) y3 = y1 - axisLimit + 1; + if (z3 - z1 >= axisLimit) z3 = z1 + axisLimit - 1; + if (z1 - z3 >= axisLimit) z3 = z1 - axisLimit + 1; + //Add diagonal line from first to third - list.addAll(getDiagonalLineBlocks(player , firstPos, thirdPos, 10)); + list.addAll(getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)); } return list; } - + + //Finds the place of the second block pos + protected abstract BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace); + + //Finds the place of the third block pos + protected abstract BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace); + + //After first and second pos are known, we want to visualize the blocks in a way (like floor for cube) + protected abstract List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2); + + //After first, second and third pos are known, we want all the blocks + protected abstract List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3); + //Finds height after floor has been chosen in buildmodes with 3 clicks public static BlockPos findHeight(EntityPlayer player, BlockPos secondPos, boolean skipRaytrace) { Vec3d look = player.getLookVec(); @@ -182,47 +212,4 @@ public class DiagonalLine implements IBuildMode { } return new BlockPos(selected.lineBound); } - - //Add diagonal line from first to second - public static List getDiagonalLineBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, float sampleMultiplier) { - List list = new ArrayList<>(); - - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y1 = firstPos.getY(), y2 = secondPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - Vec3d first = new Vec3d(x1, y1, z1).add(0.5, 0.5, 0.5); - Vec3d second = new Vec3d(x2, y2, z2).add(0.5, 0.5, 0.5); - - int iterations = (int) Math.ceil(first.distanceTo(second) * sampleMultiplier); - for (double t = 0; t <= 1.0; t += 1.0/iterations) { - Vec3d lerp = first.add(second.subtract(first).scale(t)); - BlockPos candidate = new BlockPos(lerp); - //Only add if not equal to the last in the list - if (list.isEmpty() || !list.get(list.size() - 1).equals(candidate)) - list.add(candidate); - } - - return list; - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} \ No newline at end of file +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java new file mode 100644 index 0000000..265989f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java @@ -0,0 +1,86 @@ +package nl.requios.effortlessbuilding.buildmode; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.UUID; + +public abstract class TwoClicksBuildMode extends BaseBuildMode { + + @Override + public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { + List list = new ArrayList<>(); + + Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUniqueID()); + rightClickNr++; + rightClickTable.put(player.getUniqueID(), rightClickNr); + + if (rightClickNr == 1) { + //If clicking in air, reset and try again + if (blockPos == null) { + rightClickTable.put(player.getUniqueID(), 0); + return list; + } + + //First click, remember starting position + firstPosTable.put(player.getUniqueID(), blockPos); + sideHitTable.put(player.getUniqueID(), sideHit); + hitVecTable.put(player.getUniqueID(), hitVec); + //Keep list empty, dont place any blocks yet + } else { + //Second click, place blocks + list = findCoordinates(player, blockPos, skipRaytrace); + rightClickTable.put(player.getUniqueID(), 0); + } + + return list; + } + + @Override + public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { + List list = new ArrayList<>(); + Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; + int rightClickNr = rightClickTable.get(player.getUniqueID()); + BlockPos firstPos = firstPosTable.get(player.getUniqueID()); + + if (rightClickNr == 0) { + if (blockPos != null) + list.add(blockPos); + } else { + BlockPos secondPos = findSecondPos(player, firstPos, skipRaytrace); + if (secondPos == null) return list; + + //Limit amount of blocks we can place per row + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + int x1 = firstPos.getX(), x2 = secondPos.getX(); + int y1 = firstPos.getY(), y2 = secondPos.getY(); + int z1 = firstPos.getZ(), z2 = secondPos.getZ(); + + //limit axis + if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; + if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; + if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; + if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; + if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; + if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; + + list.addAll(getAllBlocks(player, x1, y1, z1, x2, y2, z2)); + } + + return list; + } + + //Finds the place of the second block pos based on criteria (floor must be on same height as first click, wall on same plane etc) + protected abstract BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace); + + //After first and second pos are known, we want all the blocks + protected abstract List getAllBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2); +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java deleted file mode 100644 index efe6665..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Wall.java +++ /dev/null @@ -1,226 +0,0 @@ -package nl.requios.effortlessbuilding.buildmode; - -import net.minecraft.entity.player.EntityPlayer; -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.helper.ReachHelper; - -import java.util.*; - -public class Wall implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); - - static 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(); - Vec3d wall = this.planeBound.subtract(new Vec3d(firstPos)); - this.angle = wall.x * look.x + wall.z * look.z; //dot product ignoring y (looking up/down should not affect this angle) - } - - //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 > 2 && distToPlayerSq < reach * reach && - !intersects; - } - } - - @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findWall(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - //Add whole wall - list.addAll(getWallBlocks(player, firstPos, secondPos)); - } - - return list; - } - - public static 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 - //Limit the angle to not be too extreme - for (int i = 1; i < criteriaList.size(); i++) { - Criteria criteria = criteriaList.get(i); - if (criteria.distToPlayerSq < selected.distToPlayerSq && Math.abs(criteria.angle) - Math.abs(selected.angle) < 3) - selected = criteria; - } - } - - return new BlockPos(selected.planeBound); - } - - public static List getWallBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { - List list = new ArrayList<>(); - - //Limit amount of blocks we can place per row - int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); - - int x1 = firstPos.getX(), x2 = secondPos.getX(); - int y1 = firstPos.getY(), y2 = secondPos.getY(); - int z1 = firstPos.getZ(), z2 = secondPos.getZ(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - - if (x1 == x2) { - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addXWallBlocks(list, x1, y1, y2, z1, z2); - else - addXHollowWallBlocks(list, x1, y1, y2, z1, z2); - } else { - if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) - addZWallBlocks(list, x1, x2, y1, y2, z1); - else - addZHollowWallBlocks(list, x1, x2, y1, y2, z1); - } - - return list; - } - - public static void addXWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { - - for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { - - for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - } - - public static void addZWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { - - for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { - - for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { - list.add(new BlockPos(x, y, z)); - } - } - } - - public static void addXHollowWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { - Line.addZLineBlocks(list, z1, z2, x, y1); - Line.addZLineBlocks(list, z1, z2, x, y2); - Line.addYLineBlocks(list, y1, y2, x, z1); - Line.addYLineBlocks(list, y1, y2, x, z2); - } - - public static void addZHollowWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { - Line.addXLineBlocks(list, x1, x2, y1, z); - Line.addXLineBlocks(list, x1, x2, y2, z); - Line.addYLineBlocks(list, y1, y2, x1, z); - Line.addYLineBlocks(list, y1, y2, x2, z); - } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java new file mode 100644 index 0000000..7df5a88 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Circle.java @@ -0,0 +1,88 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; + +import java.util.*; + +public class Circle extends TwoClicksBuildMode { + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + protected List getAllBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + } + + public static List getCircleBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + float centerX = x1; + float centerZ = z1; + + //Adjust for CIRCLE_START + if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) { + centerX = x1 + (x2 - x1) / 2f; + centerZ = z1 + (z2 - z1) / 2f; + } else { + x1 = (int) (centerX - (x2 - centerX)); + z1 = (int) (centerZ - (z2 - centerZ)); + } + + float radiusX = MathHelper.abs(x2 - centerX); + float radiusZ = MathHelper.abs(z2 - centerZ); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ); + else + addHollowCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ); + + return list; + } + + public static void addCircleBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + float distance = distance(l, n, centerX, centerZ); + float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n); + if (distance < radius + 0.4f) + list.add(new BlockPos(l, y1, n)); + } + } + } + + public static void addHollowCircleBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, float centerX, float centerZ, float radiusX, float radiusZ) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + float distance = distance(l, n, centerX, centerZ); + float radius = calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, l, n); + if (distance < radius + 0.4f && distance > radius - 0.6f) + list.add(new BlockPos(l, y1, n)); + } + } + } + + private static float distance(float x1, float z1, float x2, float z2) { + return MathHelper.sqrt((x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1)); + } + + public static float calculateEllipseRadius(float centerX, float centerZ, float radiusX, float radiusZ, int x, int z) { + //https://math.stackexchange.com/questions/432902/how-to-get-the-radius-of-an-ellipse-at-a-specific-angle-by-knowing-its-semi-majo + float theta = (float) MathHelper.atan2(z - centerZ, x - centerX); + float part1 = radiusX * radiusX * MathHelper.sin(theta) * MathHelper.sin(theta); + float part2 = radiusZ * radiusZ * MathHelper.cos(theta) * MathHelper.cos(theta); + return radiusX * radiusZ / MathHelper.sqrt(part1 + part2); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java new file mode 100644 index 0000000..95914b2 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cube.java @@ -0,0 +1,104 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.*; + +public class Cube extends ThreeClicksBuildMode { + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + protected BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + protected List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getFloorBlocksUsingCubeFill(player, x1, y1, z1, x2, y2, z2); + } + + @Override + protected List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + return getCubeBlocks(player, x1, y1, z1, x3, y3, z3); + } + + public static List getFloorBlocksUsingCubeFill(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON) + Floor.addHollowFloorBlocks(list, x1, x2, y1, z1, z2); + else + Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); + + return list; + } + + public static List getCubeBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + switch (ModeOptions.getCubeFill()) { + case CUBE_FULL: + addCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + case CUBE_HOLLOW: + addHollowCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + case CUBE_SKELETON: + addSkeletonCubeBlocks(list, x1, x2, y1, y2, z1, z2); + break; + } + + return list; + } + + public static void addCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(l, m, n)); + } + } + } + } + + public static void addHollowCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + Wall.addXWallBlocks(list, x1, y1, y2, z1, z2); + Wall.addXWallBlocks(list, x2, y1, y2, z1, z2); + + Wall.addZWallBlocks(list, x1, x2, y1, y2, z1); + Wall.addZWallBlocks(list, x1, x2, y1, y2, z2); + + Floor.addFloorBlocks(list, x1, x2, y1, z1, z2); + Floor.addFloorBlocks(list, x1, x2, y2, z1, z2); + } + + public static void addSkeletonCubeBlocks(List list, int x1, int x2, int y1, int y2, int z1, int z2) { + Line.addXLineBlocks(list, x1, x2, y1, z1); + Line.addXLineBlocks(list, x1, x2, y1, z2); + Line.addXLineBlocks(list, x1, x2, y2, z1); + Line.addXLineBlocks(list, x1, x2, y2, z2); + + Line.addYLineBlocks(list, y1, y2, x1, z1); + Line.addYLineBlocks(list, y1, y2, x1, z2); + Line.addYLineBlocks(list, y1, y2, x2, z1); + Line.addYLineBlocks(list, y1, y2, x2, z2); + + Line.addZLineBlocks(list, z1, z2, x1, y1); + Line.addZLineBlocks(list, z1, z2, x1, y2); + Line.addZLineBlocks(list, z1, z2, x2, y1); + Line.addZLineBlocks(list, z1, z2, x2, y2); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java new file mode 100644 index 0000000..c57b62f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Cylinder.java @@ -0,0 +1,50 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Cylinder extends ThreeClicksBuildMode { + + @Override + public BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + public BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + public List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + } + + @Override + public List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + return getCylinderBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3); + } + + public static List getCylinderBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + //Get circle blocks (using CIRCLE_START and FILL options built-in) + List circleBlocks = Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + + int lowest = Math.min(y1, y3); + int highest = Math.max(y1, y3); + + //Copy circle on y axis + for (int y = lowest; y <= highest; y++) { + for (BlockPos blockPos : circleBlocks) { + list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); + } + } + + return list; + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java new file mode 100644 index 0000000..e95fa38 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalLine.java @@ -0,0 +1,54 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +public class DiagonalLine extends ThreeClicksBuildMode { + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + protected BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + protected List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + //Add diagonal line from first to second + return getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 10); + } + + @Override + protected List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + //Add diagonal line from first to third + return getDiagonalLineBlocks(player, x1, y1, z1, x3, y3, z3, 10); + } + + //Add diagonal line from first to second + public static List getDiagonalLineBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, float sampleMultiplier) { + List list = new ArrayList<>(); + + Vec3d first = new Vec3d(x1, y1, z1).add(0.5, 0.5, 0.5); + Vec3d second = new Vec3d(x2, y2, z2).add(0.5, 0.5, 0.5); + + int iterations = (int) Math.ceil(first.distanceTo(second) * sampleMultiplier); + for (double t = 0; t <= 1.0; t += 1.0/iterations) { + Vec3d lerp = first.add(second.subtract(first).scale(t)); + BlockPos candidate = new BlockPos(lerp); + //Only add if not equal to the last in the list + if (list.isEmpty() || !list.get(list.size() - 1).equals(candidate)) + list.add(candidate); + } + + return list; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java new file mode 100644 index 0000000..6a4cebb --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/DiagonalWall.java @@ -0,0 +1,52 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +public class DiagonalWall extends ThreeClicksBuildMode { + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + protected BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + protected List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1); + } + + @Override + protected List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + return getDiagonalWallBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3); + } + + //Add diagonal wall from first to second + public static List getDiagonalWallBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1); + + int lowest = Math.min(y1, y3); + int highest = Math.max(y1, y3); + + //Copy diagonal line on y axis + for (int y = lowest; y <= highest; y++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(blockPos.getX(), y, blockPos.getZ())); + } + } + + return list; + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java new file mode 100644 index 0000000..89f4d04 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Floor.java @@ -0,0 +1,93 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.*; + +public class Floor extends TwoClicksBuildMode { + + static 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) { + + return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq); + } + } + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return findFloor(player, firstPos, skipRaytrace); + } + + public static 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 + protected List getAllBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getFloorBlocks(player, x1, y1, z1, x2, y2, z2); + } + + public static List getFloorBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addFloorBlocks(list, x1, x2, y1, z1, z2); + else + addHollowFloorBlocks(list, x1, x2, y1, z1, z2); + + return list; + } + + public static void addFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { + + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + list.add(new BlockPos(l, y, n)); + } + } + } + + public static void addHollowFloorBlocks(List list, int x1, int x2, int y, int z1, int z2) { + Line.addXLineBlocks(list, x1, x2, y, z1); + Line.addXLineBlocks(list, x1, x2, y, z2); + Line.addZLineBlocks(list, z1, z2, x1, y); + Line.addZLineBlocks(list, z1, z2, x2, y); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java similarity index 52% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java index 2da9bc1..5d0e3ad 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Line.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Line.java @@ -1,22 +1,16 @@ -package nl.requios.effortlessbuilding.buildmode; +package nl.requios.effortlessbuilding.buildmode.buildmodes; import net.minecraft.entity.player.EntityPlayer; -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.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; import nl.requios.effortlessbuilding.helper.ReachHelper; -import java.util.*; +import java.util.ArrayList; +import java.util.List; -public class Line implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - private Dictionary rightClickClientTable = new Hashtable<>(); - private Dictionary rightClickServerTable = new Hashtable<>(); - private Dictionary firstPosTable = new Hashtable<>(); - private Dictionary sideHitTable = new Hashtable<>(); - private Dictionary hitVecTable = new Hashtable<>(); +public class Line extends TwoClicksBuildMode { static class Criteria { Vec3d planeBound; @@ -55,78 +49,14 @@ public class Line implements IBuildMode { //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 > 2 && distToPlayerSq < reach * reach && - !intersects; + return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq); } + } @Override - public void initialize(EntityPlayer player) { - rightClickClientTable.put(player.getUniqueID(), 0); - rightClickServerTable.put(player.getUniqueID(), 0); - firstPosTable.put(player.getUniqueID(), BlockPos.ORIGIN); - sideHitTable.put(player.getUniqueID(), EnumFacing.UP); - hitVecTable.put(player.getUniqueID(), Vec3d.ZERO); - } - - @Override - public List onRightClick(EntityPlayer player, BlockPos blockPos, EnumFacing sideHit, Vec3d hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); - - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - rightClickNr++; - rightClickTable.put(player.getUniqueID(), rightClickNr); - - if (rightClickNr == 1) { - //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUniqueID(), 0); - return list; - } - - //First click, remember starting position - firstPosTable.put(player.getUniqueID(), blockPos); - sideHitTable.put(player.getUniqueID(), sideHit); - hitVecTable.put(player.getUniqueID(), hitVec); - //Keep list empty, dont place any blocks yet - } else { - //Second click, place wall - - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUniqueID(), 0); - } - - return list; - } - - @Override - public List findCoordinates(EntityPlayer player, BlockPos blockPos, boolean skipRaytrace) { - List list = new ArrayList<>(); - Dictionary rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUniqueID()); - BlockPos firstPos = firstPosTable.get(player.getUniqueID()); - - if (rightClickNr == 0) { - if (blockPos != null) - list.add(blockPos); - } else { - BlockPos secondPos = findLine(player, firstPos, skipRaytrace); - if (secondPos == null) return list; - - list.addAll(getLineBlocks(player, firstPos, secondPos)); - } - - return list; + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return findLine(player, firstPos, skipRaytrace); } public static BlockPos findLine(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { @@ -178,25 +108,14 @@ public class Line implements IBuildMode { return new BlockPos(selected.lineBound); } - public static List getLineBlocks(EntityPlayer player, BlockPos firstPos, BlockPos secondPos) { + @Override + protected List getAllBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getLineBlocks(player, x1, y1, z1, x2, y2, z2); + } + + public static List getLineBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { List list = new ArrayList<>(); - //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(); - - //limit axis - if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1; - if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1; - if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1; - if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1; - if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1; - if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1; - if (x1 != x2) { addXLineBlocks(list, x1, x2, y1, z1); } else if (y1 != y2) { @@ -225,15 +144,4 @@ public class Line implements IBuildMode { list.add(new BlockPos(x, y, z)); } } - - @Override - public EnumFacing getSideHit(EntityPlayer player) { - return sideHitTable.get(player.getUniqueID()); - } - - @Override - public Vec3d getHitVec(EntityPlayer player) { - return hitVecTable.get(player.getUniqueID()); - } - } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java similarity index 89% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java index 430cb1b..f917a32 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/Normal.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Normal.java @@ -1,9 +1,10 @@ -package nl.requios.effortlessbuilding.buildmode; +package nl.requios.effortlessbuilding.buildmode.buildmodes; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java similarity index 89% rename from src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java rename to src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java index a503efa..63ec7e5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/NormalPlus.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/NormalPlus.java @@ -1,9 +1,10 @@ -package nl.requios.effortlessbuilding.buildmode; +package nl.requios.effortlessbuilding.buildmode.buildmodes; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.IBuildMode; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java new file mode 100644 index 0000000..df7c78e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/SlopeFloor.java @@ -0,0 +1,95 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.ArrayList; +import java.util.List; + +public class SlopeFloor extends ThreeClicksBuildMode { + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + protected BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + protected List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2); + } + + @Override + protected List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + return getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3); + } + + //Add slope floor from first to second + public static List getSlopeFloorBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + int axisLimit = ReachHelper.getMaxBlocksPerAxis(player); + + //Determine whether to use x or z axis to slope up + boolean onXAxis = true; + + int xLength = Math.abs(x2 - x1); + int zLength = Math.abs(z2 - z1); + + if (ModeOptions.getRaisedEdge() == ModeOptions.ActionEnum.SHORT_EDGE) { + //Slope along short edge + if (zLength > xLength) onXAxis = false; + } else { + //Slope along long edge + if (zLength <= xLength) onXAxis = false; + } + + if (onXAxis) { + //Along X goes up + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y3, z1, 1f); + + //Limit amount of blocks we can place + int lowest = Math.min(z1, z2); + int highest = Math.max(z1, z2); + + if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; + + //Copy diagonal line on x axis + for (int z = lowest; z <= highest; z++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(blockPos.getX(), blockPos.getY(), z)); + } + } + + } else { + //Along Z goes up + + //Get diagonal line blocks + List diagonalLineBlocks = DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x1, y3, z2, 1f); + + //Limit amount of blocks we can place + int lowest = Math.min(x1, x2); + int highest = Math.max(x1, x2); + + if (highest - lowest >= axisLimit) highest = lowest + axisLimit - 1; + + //Copy diagonal line on x axis + for (int x = lowest; x <= highest; x++) { + for (BlockPos blockPos : diagonalLineBlocks) { + list.add(new BlockPos(x, blockPos.getY(), blockPos.getZ())); + } + } + } + + return list; + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java new file mode 100644 index 0000000..4d3c24f --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Sphere.java @@ -0,0 +1,110 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode; + +import java.util.ArrayList; +import java.util.List; + +public class Sphere extends ThreeClicksBuildMode { + + @Override + public BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return Floor.findFloor(player, firstPos, skipRaytrace); + } + + @Override + public BlockPos findThirdPos(EntityPlayer player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) { + return findHeight(player, secondPos, skipRaytrace); + } + + @Override + public List getIntermediateBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2); + } + + @Override + public List getFinalBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + return getSphereBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3); + } + + public static List getSphereBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) { + List list = new ArrayList<>(); + + float centerX = x1; + float centerY = y1; + float centerZ = z1; + + //Adjust for CIRCLE_START + if (ModeOptions.getCircleStart() == ModeOptions.ActionEnum.CIRCLE_START_CORNER) { + centerX = x1 + (x2 - x1) / 2f; + centerY = y1 + (y3 - y1) / 2f; + centerZ = z1 + (z2 - z1) / 2f; + } else { + x1 = (int) (centerX - (x2 - centerX)); + y1 = (int) (centerY - (y3 - centerY)); + z1 = (int) (centerZ - (z2 - centerZ)); + } + + float radiusX = MathHelper.abs(x2 - centerX); + float radiusY = MathHelper.abs(y3 - centerY); + float radiusZ = MathHelper.abs(z2 - centerZ); + + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ); + else + addHollowSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ); + + return list; + } + + public static void addSphereBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, + float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + + float distance = distance(l, m, n, centerX, centerY, centerZ); + float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n); + if (distance < radius + 0.4f) + list.add(new BlockPos(l, m, n)); + } + } + } + } + + public static void addHollowSphereBlocks(List list, int x1, int y1, int z1, int x2, int y2, int z2, + float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ) { + for (int l = x1; x1 < x2 ? l <= x2 : l >= x2; l += x1 < x2 ? 1 : -1) { + + for (int n = z1; z1 < z2 ? n <= z2 : n >= z2; n += z1 < z2 ? 1 : -1) { + + for (int m = y1; y1 < y2 ? m <= y2 : m >= y2; m += y1 < y2 ? 1 : -1) { + + float distance = distance(l, m, n, centerX, centerY, centerZ); + float radius = calculateSpheroidRadius(centerX, centerY, centerZ, radiusX, radiusY, radiusZ, l, m, n); + if (distance < radius + 0.4f && distance > radius - 0.6f) + list.add(new BlockPos(l, m, n)); + } + } + } + } + + private static float distance(float x1, float y1, float z1, float x2, float y2, float z2) { + return MathHelper.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1)); + } + + public static float calculateSpheroidRadius(float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ, int x, int y, int z) { + //Twice ellipse radius + float radiusXZ = Circle.calculateEllipseRadius(centerX, centerZ, radiusX, radiusZ, x, z); + + //TODO project x to plane + + return Circle.calculateEllipseRadius(centerX, centerY, radiusXZ, radiusY, x, y); + } +} \ No newline at end of file diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java new file mode 100644 index 0000000..60da58b --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/buildmodes/Wall.java @@ -0,0 +1,134 @@ +package nl.requios.effortlessbuilding.buildmode.buildmodes; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode; +import nl.requios.effortlessbuilding.buildmode.ModeOptions; +import nl.requios.effortlessbuilding.helper.ReachHelper; + +import java.util.*; + +public class Wall extends TwoClicksBuildMode { + + static 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(); + Vec3d wall = this.planeBound.subtract(new Vec3d(firstPos)); + this.angle = wall.x * look.x + wall.z * look.z; //dot product ignoring y (looking up/down should not affect this angle) + } + + //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) { + + return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq); + } + } + + @Override + protected BlockPos findSecondPos(EntityPlayer player, BlockPos firstPos, boolean skipRaytrace) { + return findWall(player, firstPos, skipRaytrace); + } + + public static 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 + //Limit the angle to not be too extreme + for (int i = 1; i < criteriaList.size(); i++) { + Criteria criteria = criteriaList.get(i); + if (criteria.distToPlayerSq < selected.distToPlayerSq && Math.abs(criteria.angle) - Math.abs(selected.angle) < 3) + selected = criteria; + } + } + + return new BlockPos(selected.planeBound); + } + + @Override + protected List getAllBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + return getWallBlocks(player, x1, y1, z1, x2, y2, z2); + } + + public static List getWallBlocks(EntityPlayer player, int x1, int y1, int z1, int x2, int y2, int z2) { + List list = new ArrayList<>(); + + if (x1 == x2) { + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addXWallBlocks(list, x1, y1, y2, z1, z2); + else + addXHollowWallBlocks(list, x1, y1, y2, z1, z2); + } else { + if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL) + addZWallBlocks(list, x1, x2, y1, y2, z1); + else + addZHollowWallBlocks(list, x1, x2, y1, y2, z1); + } + + return list; + } + + public static void addXWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { + + for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) { + + for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + } + + public static void addZWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { + + for (int x = x1; x1 < x2 ? x <= x2 : x >= x2; x += x1 < x2 ? 1 : -1) { + + for (int y = y1; y1 < y2 ? y <= y2 : y >= y2; y += y1 < y2 ? 1 : -1) { + list.add(new BlockPos(x, y, z)); + } + } + } + + public static void addXHollowWallBlocks(List list, int x, int y1, int y2, int z1, int z2) { + Line.addZLineBlocks(list, z1, z2, x, y1); + Line.addZLineBlocks(list, z1, z2, x, y2); + Line.addYLineBlocks(list, y1, y2, x, z1); + Line.addYLineBlocks(list, y1, y2, x, z2); + } + + public static void addZHollowWallBlocks(List list, int x1, int x2, int y1, int y2, int z) { + Line.addXLineBlocks(list, x1, x2, y1, z); + Line.addXLineBlocks(list, x1, x2, y2, z); + Line.addYLineBlocks(list, y1, y2, x1, z); + Line.addYLineBlocks(list, y1, y2, x2, z); + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java index ada64e0..fc71ea0 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java @@ -206,6 +206,7 @@ public class UndoRedo { private static ItemStack findItemStackInInventory(EntityPlayer player, IBlockState blockState) { ItemStack itemStack = ItemStack.EMPTY; + if (blockState == null) return itemStack; //First try previousBlockStates //TODO try to find itemstack with right blockstate first diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java index 8b5a594..2b0ae8a 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java @@ -26,6 +26,7 @@ import net.minecraft.util.EnumFacing; import net.minecraftforge.client.model.ModelLoader; import static nl.requios.effortlessbuilding.buildmode.BuildModes.*; +import static nl.requios.effortlessbuilding.buildmode.ModeOptions.*; /** * From Chisels and Bits by AlgorithmX2 @@ -39,7 +40,7 @@ public class RadialMenu extends GuiScreen { private float visibility = 0.0f; private Stopwatch lastChange = Stopwatch.createStarted(); public BuildModeEnum switchTo = null; - public ModeOptions.ActionEnum doAction = null; + public ActionEnum doAction = null; public boolean actionUsed = false; private float clampVis(final float f) { @@ -77,11 +78,11 @@ public class RadialMenu extends GuiScreen { public double y1, y2; public boolean highlighted; - public final ModeOptions.ActionEnum action; + public final ActionEnum action; public String name; public EnumFacing textSide; - public MenuButton(final String name, final ModeOptions.ActionEnum action, final double x, final double y, + public MenuButton(final String name, final ActionEnum action, final double x, final double y, final EnumFacing textSide) { this.name = I18n.format(name); this.action = action; @@ -138,10 +139,10 @@ public class RadialMenu extends GuiScreen { final double mouseYCenter = mouseY - middleY; double mouseRadians = Math.atan2(mouseYCenter, mouseXCenter); - final double ringInnerEdge = 20; - final double ringOuterEdge = 50; - final double textDistance = 60; - final double buttonDistance = 90; + final double ringInnerEdge = 30; + final double ringOuterEdge = 65; + final double textDistance = 75; + final double buttonDistance = 105; final double quarterCircle = Math.PI / 2.0; if ( mouseRadians < -quarterCircle ) { @@ -157,16 +158,18 @@ public class RadialMenu extends GuiScreen { } //Add actions - buttons.add(new MenuButton(ModeOptions.ActionEnum.UNDO.name, ModeOptions.ActionEnum.UNDO, -buttonDistance - 26, -13, EnumFacing.UP)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.REDO.name, ModeOptions.ActionEnum.REDO, -buttonDistance, -13, EnumFacing.UP)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS.name, ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 26, 13, EnumFacing.DOWN)); - buttons.add(new MenuButton(ModeOptions.ActionEnum.REPLACE.name, ModeOptions.ActionEnum.REPLACE, -buttonDistance, 13, EnumFacing.DOWN)); + buttons.add(new MenuButton(ActionEnum.UNDO.name, ActionEnum.UNDO, -buttonDistance - 26, -13, EnumFacing.UP)); + buttons.add(new MenuButton(ActionEnum.REDO.name, ActionEnum.REDO, -buttonDistance, -13, EnumFacing.UP)); + buttons.add(new MenuButton(ActionEnum.OPEN_MODIFIER_SETTINGS.name, ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 26, 13, EnumFacing.DOWN)); + buttons.add(new MenuButton(ActionEnum.REPLACE.name, ActionEnum.REPLACE, -buttonDistance, 13, EnumFacing.DOWN)); //Add buildmode dependent options - ModeOptions.ActionEnum[] options = currentBuildMode.options; + OptionEnum[] options = currentBuildMode.options; for (int i = 0; i < options.length; i++) { - ModeOptions.ActionEnum action = options[i]; - buttons.add(new MenuButton(action.name, action, buttonDistance + i * 26, -13, EnumFacing.DOWN)); + for (int j = 0; j < options[i].actions.length; j++) { + ActionEnum action = options[i].actions[j]; + buttons.add(new MenuButton(action.name, action, buttonDistance + j * 26, -13 + i * 39, EnumFacing.DOWN)); + } } switchTo = null; @@ -245,11 +248,12 @@ public class RadialMenu extends GuiScreen { float a = 0.5f; //highlight when active option - if (btn.action == ModeOptions.getBuildSpeed() || - btn.action == ModeOptions.getFill() || - btn.action == ModeOptions.getCubeFill() || - btn.action == ModeOptions.getRaisedEdge() || - btn.action == ModeOptions.getLineThickness()) { + if (btn.action == getBuildSpeed() || + btn.action == getFill() || + btn.action == getCubeFill() || + btn.action == getRaisedEdge() || + btn.action == getLineThickness() || + btn.action == getCircleStart()) { r = 0.0f; g = 0.5f; b = 1f; @@ -342,32 +346,12 @@ public class RadialMenu extends GuiScreen { //Draw strings //fontRenderer.drawStringWithShadow("Actions", (int) (middleX - buttonDistance - 13) - fontRenderer.getStringWidth("Actions") * 0.5f, (int) middleY - 38, 0xffffffff); - String title = ""; - if (currentBuildMode.options.length > 0) { - switch (currentBuildMode.options[0]) { - case NORMAL_SPEED: - case FAST_SPEED: - title = I18n.format("effortlessbuilding.action.build_speed"); - break; - case FULL: - case HOLLOW: - case CUBE_FULL: - case CUBE_HOLLOW: - case CUBE_SKELETON: - title = I18n.format("effortlessbuilding.action.filling"); - break; - case SHORT_EDGE: - case LONG_EDGE: - title = I18n.format("effortlessbuilding.action.raised_edge"); - break; - case THICKNESS_1: - case THICKNESS_3: - case THICKNESS_5: - title = I18n.format("effortlessbuilding.action.thickness"); - break; - } + + //Draw option strings + for (int i = 0; i < currentBuildMode.options.length; i++) { + OptionEnum option = options[i]; + fontRenderer.drawStringWithShadow(I18n.format(option.name), (int) (middleX + buttonDistance - 9), (int) middleY - 37 + i * 39, 0xeeeeeeff); } - fontRenderer.drawStringWithShadow(title, (int) (middleX + buttonDistance - 9), (int) middleY - 37, 0xeeeeeeff); String credits = "Effortless Building"; fontRenderer.drawStringWithShadow(credits, width - fontRenderer.getStringWidth(credits) - 4, height - 10, 0x88888888); @@ -402,18 +386,26 @@ public class RadialMenu extends GuiScreen { String keybindFormatted = ""; //Add keybind in brackets - if (button.action == ModeOptions.ActionEnum.UNDO) { + if (button.action == ActionEnum.UNDO) { keybind = ClientProxy.keyBindings[4].getDisplayName(); } - if (button.action == ModeOptions.ActionEnum.REDO) { + if (button.action == ActionEnum.REDO) { keybind = ClientProxy.keyBindings[5].getDisplayName(); } - if (button.action == ModeOptions.ActionEnum.REPLACE) { + if (button.action == ActionEnum.REPLACE) { keybind = ClientProxy.keyBindings[1].getDisplayName(); } - if (button.action == ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS) { + if (button.action == ActionEnum.OPEN_MODIFIER_SETTINGS) { keybind = ClientProxy.keyBindings[0].getDisplayName(); } + if (currentBuildMode.options.length > 0) { + //Add (ctrl) to first two actions of first option + if (button.action == currentBuildMode.options[0].actions[0] + || button.action == currentBuildMode.options[0].actions[1]) { + keybind = ClientProxy.keyBindings[6].getDisplayName(); + if (keybind.equals("LCONTROL")) keybind = "Ctrl"; + } + } if (!keybind.isEmpty()) keybindFormatted = TextFormatting.GRAY + "(" + WordUtils.capitalizeFully(keybind) + ")"; if (button.textSide == EnumFacing.WEST) { diff --git a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java index 8c1fc0c..c5004eb 100644 --- a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java +++ b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java @@ -81,7 +81,7 @@ public class ClientProxy implements IProxy { @Override public void init(FMLInitializationEvent event) { // register key bindings - keyBindings = new KeyBinding[6]; + keyBindings = new KeyBinding[7]; // instantiate the key bindings keyBindings[0] = new KeyBinding("key.effortlessbuilding.hud.desc", KeyConflictContext.UNIVERSAL, Keyboard.KEY_ADD, "key.effortlessbuilding.category"); @@ -97,7 +97,8 @@ public class ClientProxy implements IProxy { }; keyBindings[4] = new KeyBinding("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, Keyboard.KEY_Z, "key.effortlessbuilding.category"); keyBindings[5] = new KeyBinding("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, Keyboard.KEY_Y, "key.effortlessbuilding.category"); -// keyBindings[6] = new KeyBinding("Reload shaders", Keyboard.KEY_TAB, "key.effortlessbuilding.category"); + keyBindings[6] = new KeyBinding("key.effortlessbuilding.altplacement.desc", KeyConflictContext.IN_GAME, Keyboard.KEY_LCONTROL, "key.effortlessbuilding.category"); +// keyBindings[7] = new KeyBinding("Reload shaders", Keyboard.KEY_TAB, "key.effortlessbuilding.category"); // register all the key bindings for (int i = 0; i < keyBindings.length; ++i) { @@ -344,8 +345,26 @@ public class ClientProxy implements IProxy { EffortlessBuilding.packetHandler.sendToServer(new ModeActionMessage(action)); } + //Change placement mode + if (keyBindings[6].isPressed()) { + //Toggle between first two actions of the first option of the current build mode + BuildModes.BuildModeEnum currentBuildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + if (currentBuildMode.options.length > 0) { + ModeOptions.OptionEnum option = currentBuildMode.options[0]; + if (option.actions.length >= 2) { + if (ModeOptions.getOptionSetting(option) == option.actions[0]) { + ModeOptions.performAction(player, option.actions[1]); + EffortlessBuilding.packetHandler.sendToServer(new ModeActionMessage(option.actions[1])); + } else { + ModeOptions.performAction(player, option.actions[0]); + EffortlessBuilding.packetHandler.sendToServer(new ModeActionMessage(option.actions[0])); + } + } + } + } + //For shader development - if (keyBindings.length >= 7 && keyBindings[6].isPressed()) { + if (keyBindings.length >= 8 && keyBindings[7].isPressed()) { ShaderHandler.init(); EffortlessBuilding.log(player, "Reloaded shaders"); } diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java index da251c6..f40eba5 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java @@ -195,13 +195,15 @@ public class BlockPreviewRenderer { //if so, renew randomness of randomizer bag ItemRandomizerBag.renewRandomness(); //and play sound (max once every tick) - if (startCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientProxy.ticksInGame - 0) { + if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientProxy.ticksInGame - 0) { soundTime = ClientProxy.ticksInGame; - SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.world, - newCoordinates.get(0), player); - player.world.playSound(player, player.getPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(), - SoundCategory.BLOCKS, 0.3f, 0.8f); + if (blockStates.get(0) != null) { + SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.world, + newCoordinates.get(0), player); + player.world.playSound(player, player.getPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(), + SoundCategory.BLOCKS, 0.3f, 0.8f); + } } } @@ -236,8 +238,20 @@ public class BlockPreviewRenderer { //Display block count and dimensions in actionbar if (BuildModes.isActive(player)) { - BlockPos dim = secondPos.subtract(firstPos); - dim = new BlockPos(Math.abs(dim.getX()) + 1, Math.abs(dim.getY()) + 1, Math.abs(dim.getZ()) + 1); + + //Find min and max values (not simply firstPos and secondPos because that doesn't work with circles) + int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE; + int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE; + for (BlockPos pos : startCoordinates) { + if (pos.getX() < minX) minX = pos.getX(); + if (pos.getX() > maxX) maxX = pos.getX(); + if (pos.getY() < minY) minY = pos.getY(); + if (pos.getY() > maxY) maxY = pos.getY(); + if (pos.getZ() < minZ) minZ = pos.getZ(); + if (pos.getZ() > maxZ) maxZ = pos.getZ(); + } + BlockPos dim = new BlockPos(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1); String dimensions = "("; if (dim.getX() > 1) dimensions += dim.getX() + "x"; diff --git a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java index 8386d7e..83b610e 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/RenderHandler.java @@ -206,6 +206,8 @@ public class RenderHandler implements IWorldEventListener { } protected static void renderBlockPreview(BlockRendererDispatcher dispatcher, BlockPos blockPos, IBlockState blockState) { + if (blockState == null) return; + GlStateManager.pushMatrix(); GlStateManager.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ()); GlStateManager.rotate(-90.0F, 0.0F, 1.0F, 0.0F); diff --git a/src/main/resources/assets/effortlessbuilding/lang/en_us.lang b/src/main/resources/assets/effortlessbuilding/lang/en_us.lang index f292005..662ce46 100644 --- a/src/main/resources/assets/effortlessbuilding/lang/en_us.lang +++ b/src/main/resources/assets/effortlessbuilding/lang/en_us.lang @@ -2,9 +2,10 @@ key.effortlessbuilding.category=Effortless Building key.effortlessbuilding.hud.desc=Modifier Menu key.effortlessbuilding.replace.desc=Toggle QuickReplace key.effortlessbuilding.creative.desc=Toggle Survival/Creative Mode -key.effortlessbuilding.mode.desc=Radial Menu (C&B compatible) +key.effortlessbuilding.mode.desc=Radial Menu key.effortlessbuilding.undo.desc=Undo key.effortlessbuilding.redo.desc=Redo +key.effortlessbuilding.altplacement.desc=Alternative placement item.effortlessbuilding:randomizer_bag.name=Randomizer Bag item.effortlessbuilding:reach_upgrade1.name=Reach Upgrade 1 @@ -20,6 +21,9 @@ effortlessbuilding.mode.diagonal_line=Diagonal Line effortlessbuilding.mode.diagonal_wall=Diagonal Wall effortlessbuilding.mode.slope_floor=Slope Floor effortlessbuilding.mode.cube=Cube +effortlessbuilding.mode.circle=Circle +effortlessbuilding.mode.cylinder=Cylinder +effortlessbuilding.mode.sphere=Sphere effortlessbuilding.action.undo=Undo effortlessbuilding.action.redo=Redo @@ -30,6 +34,7 @@ effortlessbuilding.action.build_speed=Build Speed effortlessbuilding.action.filling=Filling effortlessbuilding.action.raised_edge=Raised Edge effortlessbuilding.action.thickness=Line Thickness +effortlessbuilding.action.circle_start=Start Point effortlessbuilding.action.normal_speed=Normal effortlessbuilding.action.fast_speed=Fast @@ -41,5 +46,7 @@ effortlessbuilding.action.long_edge=Long Edge effortlessbuilding.action.thickness_1=1 Block Thick effortlessbuilding.action.thickness_3=3 Blocks Thick effortlessbuilding.action.thickness_5=5 Blocks Thick +effortlessbuilding.action.start_center=Middle +effortlessbuilding.action.start_corner=Corner commands.reach.usage=/reach \ No newline at end of file diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/circle.png b/src/main/resources/assets/effortlessbuilding/textures/icons/circle.png new file mode 100644 index 0000000..66d5a04 Binary files /dev/null and b/src/main/resources/assets/effortlessbuilding/textures/icons/circle.png differ diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_center.png b/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_center.png new file mode 100644 index 0000000..6f1a623 Binary files /dev/null and b/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_center.png differ diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_corner.png b/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_corner.png new file mode 100644 index 0000000..f64e3eb Binary files /dev/null and b/src/main/resources/assets/effortlessbuilding/textures/icons/circle_start_corner.png differ diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/cylinder.png b/src/main/resources/assets/effortlessbuilding/textures/icons/cylinder.png new file mode 100644 index 0000000..4964d33 Binary files /dev/null and b/src/main/resources/assets/effortlessbuilding/textures/icons/cylinder.png differ diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/sphere.png b/src/main/resources/assets/effortlessbuilding/textures/icons/sphere.png new file mode 100644 index 0000000..73a804a Binary files /dev/null and b/src/main/resources/assets/effortlessbuilding/textures/icons/sphere.png differ