From 2cac2be29f0ab9162db8a704bd8e720594553d5a Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Fri, 15 Mar 2019 20:46:47 +0100 Subject: [PATCH] Added undo, redo functionality. All placements use blocks from entire inventory now. Added block count and dimensions in actionbar when placing/breaking blocks. Added undo, redo, replace and 'open modifier settings' icons. Fixed not being able to break blocks in creative when starting the selection on a tallgrass (instabreaking) block. --- build.gradle | 2 +- .../effortlessbuilding/BuildConfig.java | 4 + .../EffortlessBuilding.java | 2 +- .../buildmode/BuildModes.java | 21 +++- .../buildmode/ModeOptions.java | 6 +- .../buildmodifier/BlockSet.java | 45 +++++++ .../buildmodifier/BuildModifiers.java | 43 ++++--- .../buildmodifier/UndoRedo.java | 118 ++++++++++++++++++ .../gui/buildmode/RadialMenu.java | 32 +++-- .../effortlessbuilding/helper/FixedStack.java | 52 ++++++++ .../helper/InventoryHelper.java | 30 +++++ .../effortlessbuilding/proxy/ClientProxy.java | 17 ++- .../render/BlockPreviewRenderer.java | 81 +++++++++--- .../assets/effortlessbuilding/lang/en_us.lang | 5 +- .../textures/icons/open_modifier_settings.png | Bin 317 -> 1353 bytes .../textures/icons/redo.png | Bin 317 -> 1239 bytes .../textures/icons/replace.png | Bin 317 -> 1262 bytes .../textures/icons/undo.png | Bin 317 -> 1249 bytes 18 files changed, 399 insertions(+), 59 deletions(-) create mode 100644 src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/helper/FixedStack.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/helper/InventoryHelper.java diff --git a/build.gradle b/build.gradle index 3505fa0..5fceb51 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.4" +version = "1.12.2-2.5" 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/BuildConfig.java b/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java index b9fc71f..6b882e4 100644 --- a/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java +++ b/src/main/java/nl/requios/effortlessbuilding/BuildConfig.java @@ -52,6 +52,10 @@ public class BuildConfig { "The block in front of you always counts as 100%."}) @RangeInt(min = 0, max = 200) public int miningTimePercentage = 50; + + @Comment({"How many placements are remembered for the undo functionality."}) + @RequiresMcRestart + public int undoStackSize = 5; } public static class Visuals { diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index d914fe4..c3ef1bb 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.4"; + public static final String VERSION = "1.12.2-2.5"; @Mod.Instance(EffortlessBuilding.MODID) public static EffortlessBuilding instance; diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index 1c6b98d..f45d93c 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -119,8 +119,7 @@ public class BuildModes { //Use a network message to break blocks in the distance using clientside mouse input public static void onBlockBrokenMessage(EntityPlayer player, BlockBrokenMessage message) { - if (ReachHelper.canBreakFar(player) && - !CompatHelper.chiselsAndBitsProxy.isHoldingChiselTool(EnumHand.MAIN_HAND)) { + if (ReachHelper.canBreakFar(player)) { BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null; onBlockBroken(player, startPos, true); @@ -132,7 +131,7 @@ public class BuildModes { //Check if not in the middle of placing Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) { - //Cancel placing + //Cancel breaking initializeMode(player); return; } @@ -177,4 +176,20 @@ public class BuildModes { ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player); } + + public static boolean isCurrentlyPlacing(EntityPlayer player) { + Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player); + } + + public static boolean isCurrentlyBreaking(EntityPlayer player) { + Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null && currentlyBreaking.get(player); + } + + //Either placing or breaking + public static boolean isActive(EntityPlayer player) { + Dictionary currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer; + return currentlyBreaking.get(player) != null; + } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java index 6fd2858..34f3f31 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeOptions.java @@ -7,6 +7,7 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu; import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui; @@ -21,12 +22,15 @@ public class ModeOptions { //Called on both client and server public static void performAction(EntityPlayer player, ActionEnum action) { - EffortlessBuilding.log("Doing "+action.name()); switch (action) { case UNDO: + UndoRedo.undo(player); + EffortlessBuilding.log(player, "Undo", true); break; case REDO: + UndoRedo.redo(player); + EffortlessBuilding.log(player, "Redo", true); break; case REPLACE: ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java new file mode 100644 index 0000000..6098f2e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java @@ -0,0 +1,45 @@ +package nl.requios.effortlessbuilding.buildmodifier; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +import java.util.List; + +public class BlockSet { + private List coordinates; + private List blockStates; + private Vec3d hitVec; + private BlockPos firstPos; + private BlockPos secondPos; + + public BlockSet(List coordinates, List blockStates, Vec3d hitVec, + BlockPos firstPos, BlockPos secondPos) { + this.coordinates = coordinates; + this.blockStates = blockStates; + this.hitVec = hitVec; + this.firstPos = firstPos; + this.secondPos = secondPos; + } + + public List getCoordinates() { + return coordinates; + } + + public List getBlockStates() { + return blockStates; + } + + public Vec3d getHitVec() { + return hitVec; + } + + public BlockPos getFirstPos() { + return firstPos; + } + + public BlockPos getSecondPos() { + return secondPos; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java index f154e98..32916a7 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java @@ -11,19 +11,21 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import nl.requios.effortlessbuilding.BuildConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.helper.FixedStack; +import nl.requios.effortlessbuilding.helper.InventoryHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.item.ItemRandomizerBag; import nl.requios.effortlessbuilding.network.BlockPlacedMessage; import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; public class BuildModifiers { + //Called from BuildModes public static void onBlockPlaced(EntityPlayer player, List startCoordinates, EnumFacing sideHit, Vec3d hitVec, boolean placeStartPos) { World world = player.world; @@ -41,23 +43,33 @@ public class BuildModifiers { if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return; if (world.isRemote) { + BlockPreviewRenderer.onBlocksPlaced(); - return; - } - //place blocks - for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) { - BlockPos blockPos = coordinates.get(i); - IBlockState blockState = blockStates.get(i); - ItemStack itemStack = itemStacks.get(i); + } else { - if (world.isBlockLoaded(blockPos, true)) { - //check itemstack empty - if (itemStack.isEmpty()) continue; - SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, EnumFacing.UP, hitVec, false, false); + //place blocks + for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) { + BlockPos blockPos = coordinates.get(i); + IBlockState blockState = blockStates.get(i); + ItemStack itemStack = itemStacks.get(i); + + if (world.isBlockLoaded(blockPos, true)) { + //check itemstack empty + if (itemStack.isEmpty()) { + //try to find new stack, otherwise continue + itemStack = InventoryHelper.findItemStackInInventory(player, blockState); + if (itemStack.isEmpty()) continue; + } + SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, EnumFacing.UP, hitVec, false, false); + } } } + //add to undo stack + BlockPos firstPos = startCoordinates.get(0); + BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1); + UndoRedo.addUndo(player, new BlockSet(coordinates, blockStates, hitVec, firstPos, secondPos)); } public static void onBlockBroken(EntityPlayer player, List posList, boolean breakStartPos) { @@ -73,7 +85,8 @@ public class BuildModifiers { } //If the player is going to instabreak grass or a plant, only break other instabreaking things - boolean onlyInstaBreaking = world.getBlockState(posList.get(0)).getBlockHardness(world, posList.get(0)) == 0f; + boolean onlyInstaBreaking = !player.isCreative() && + world.getBlockState(posList.get(0)).getBlockHardness(world, posList.get(0)) == 0f; //break all those blocks for (int i = breakStartPos ? 0 : 1; i < coordinates.size(); i++) { diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java new file mode 100644 index 0000000..dba5dd5 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/UndoRedo.java @@ -0,0 +1,118 @@ +package nl.requios.effortlessbuilding.buildmodifier; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import nl.requios.effortlessbuilding.BuildConfig; +import nl.requios.effortlessbuilding.helper.FixedStack; +import nl.requios.effortlessbuilding.helper.InventoryHelper; +import nl.requios.effortlessbuilding.helper.SurvivalHelper; +import nl.requios.effortlessbuilding.render.BlockPreviewRenderer; + +import java.util.*; + +public class UndoRedo { + + private static Map> undoStacks = new HashMap<>(); + private static Map> redoStacks = new HashMap<>(); + + //add to undo stack + public static void addUndo(EntityPlayer player, BlockSet blockSet) { + + //If no stack exists, make one + if (!undoStacks.containsKey(player.getUniqueID())) { + undoStacks.put(player.getUniqueID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize])); + } + + undoStacks.get(player.getUniqueID()).push(blockSet); + } + + private static void addRedo(EntityPlayer player, BlockSet blockSet) { + + //If no stack exists, make one + if (!redoStacks.containsKey(player.getUniqueID())) { + redoStacks.put(player.getUniqueID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize])); + } + + redoStacks.get(player.getUniqueID()).push(blockSet); + } + + public static boolean undo(EntityPlayer player) { + if (!undoStacks.containsKey(player.getUniqueID())) return false; + + FixedStack undoStack = undoStacks.get(player.getUniqueID()); + + if (undoStack.isEmpty()) return false; + + BlockSet blockSet = undoStack.pop(); + List coordinates = blockSet.getCoordinates(); + List blockStates = blockSet.getBlockStates(); + + //Find up to date itemstacks in player inventory + List itemStacks = findItemStacksInInventory(player, blockStates); + + //break all those blocks + for (int i = 0; i < coordinates.size(); i++) { + BlockPos coordinate = coordinates.get(i); + if (player.world.isBlockLoaded(coordinate, false)) { + SurvivalHelper.breakBlock(player.world, player, coordinate); + } + } + + if (player.world.isRemote) + BlockPreviewRenderer.onBlocksBroken(coordinates, itemStacks, blockStates, blockSet.getSecondPos(), blockSet.getFirstPos()); + + //add to redo + addRedo(player, blockSet); + + return true; + } + + public static boolean redo(EntityPlayer player) { + if (!redoStacks.containsKey(player.getUniqueID())) return false; + + FixedStack redoStack = redoStacks.get(player.getUniqueID()); + + if (redoStack.isEmpty()) return false; + + BlockSet blockSet = redoStack.pop(); + List coordinates = blockSet.getCoordinates(); + List blockStates = blockSet.getBlockStates(); + Vec3d hitVec = blockSet.getHitVec(); + + //Find up to date itemstacks in player inventory + List itemStacks = findItemStacksInInventory(player, blockStates); + + //place blocks + for (int i = 0; i < coordinates.size(); i++) { + BlockPos blockPos = coordinates.get(i); + IBlockState blockState = blockStates.get(i); + ItemStack itemStack = itemStacks.get(i); + + if (player.world.isBlockLoaded(blockPos, true)) { + //check itemstack empty + if (itemStack.isEmpty()) continue; + SurvivalHelper.placeBlock(player.world, player, blockPos, blockState, itemStack, EnumFacing.UP, hitVec, false, false); + } + } + + if (player.world.isRemote) + BlockPreviewRenderer.onBlocksPlaced(coordinates, itemStacks, blockStates, blockSet.getFirstPos(), blockSet.getSecondPos()); + + //add to undo + addUndo(player, blockSet); + + return true; + } + + private static List findItemStacksInInventory(EntityPlayer player, List blockStates) { + List itemStacks = new ArrayList<>(blockStates.size()); + for (IBlockState blockState : blockStates) { + itemStacks.add(InventoryHelper.findItemStackInInventory(player, blockState)); + } + return itemStacks; + } +} 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 f4e5aac..d4b966b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java @@ -265,12 +265,10 @@ public class RadialMenu extends GuiScreen { final TextureAtlasSprite sprite = ClientProxy.getBuildModeIcon(menuRegion.mode); - final double scalex = 16 * 0.5; - final double scaley = 16 * 0.5; - final double x1 = x - scalex; - final double x2 = x + scalex; - final double y1 = y - scaley; - final double y2 = y + scaley; + final double x1 = x - 8; + final double x2 = x + 8; + final double y1 = y - 8; + final double y2 = y + 8; final float f = 1f; final float a = 1f; @@ -298,8 +296,8 @@ public class RadialMenu extends GuiScreen { final TextureAtlasSprite sprite = ClientProxy.getModeOptionIcon(button.action); - final double btnmiddleX = (button.x1 + button.x2) / 2.0; - final double btnmiddleY = (button.y1 + button.y2) / 2.0; + final double btnmiddleX = (button.x1 + button.x2) / 2 + 0.01; + final double btnmiddleY = (button.y1 + button.y2) / 2 + 0.01; final double btnx1 = btnmiddleX - 8; final double btnx2 = btnmiddleX + 8; final double btny1 = btnmiddleY - 8; @@ -337,15 +335,23 @@ public class RadialMenu extends GuiScreen { if (button.highlighted) { String text = TextFormatting.AQUA + button.name; int wrap = 120; - String keybind = TextFormatting.GRAY + "(None)"; + String keybind = "None"; //Add keybind in brackets + if (button.action == ModeOptions.ActionEnum.UNDO) { + keybind = ClientProxy.keyBindings[4].getDisplayName(); + } + if (button.action == ModeOptions.ActionEnum.REDO) { + String sneak = Minecraft.getMinecraft().gameSettings.keyBindSneak.getDisplayName(); + keybind = sneak + " + " + ClientProxy.keyBindings[4].getDisplayName(); + } if (button.action == ModeOptions.ActionEnum.REPLACE) { - keybind = TextFormatting.GRAY + " (" + WordUtils.capitalizeFully(ClientProxy.keyBindings[1].getDisplayName().toLowerCase()) + ")"; + keybind = ClientProxy.keyBindings[1].getDisplayName(); } if (button.action == ModeOptions.ActionEnum.OPEN_MODIFIER_SETTINGS) { - keybind = TextFormatting.GRAY + " (" + WordUtils.capitalizeFully(ClientProxy.keyBindings[0].getDisplayName()) + ")"; + keybind = ClientProxy.keyBindings[0].getDisplayName(); } + String keybindFormatted = TextFormatting.GRAY + " (" + WordUtils.capitalizeFully(keybind) + ")"; if (button.textSide == EnumFacing.WEST) { @@ -362,7 +368,7 @@ public class RadialMenu extends GuiScreen { fontRenderer.drawSplitString( text, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(text) * 0.5), (int) (middleY + button.y1 - 26), wrap,0xffffffff); - fontRenderer.drawSplitString( keybind, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybind) * 0.5), + fontRenderer.drawSplitString( keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybindFormatted) * 0.5), (int) (middleY + button.y1 - 14), wrap,0xffffffff); } else if (button.textSide == EnumFacing.DOWN || button.textSide == EnumFacing.SOUTH) { @@ -370,7 +376,7 @@ public class RadialMenu extends GuiScreen { fontRenderer.drawSplitString(text, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(text) * 0.5), (int) (middleY + button.y1 + 24), wrap, 0xffffffff); - fontRenderer.drawSplitString(keybind, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybind) * 0.5), + fontRenderer.drawSplitString(keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - fontRenderer.getStringWidth(keybindFormatted) * 0.5), (int) (middleY + button.y1 + 36), wrap, 0xffffffff); } diff --git a/src/main/java/nl/requios/effortlessbuilding/helper/FixedStack.java b/src/main/java/nl/requios/effortlessbuilding/helper/FixedStack.java new file mode 100644 index 0000000..473ade2 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/helper/FixedStack.java @@ -0,0 +1,52 @@ +package nl.requios.effortlessbuilding.helper; + +//Stack with fixed size. Removes (overwrites) oldest element on push. +public class FixedStack { + private T[] stack; + private int size; + private int top; + private int filled = 0; //how many valid items are in the stack + + public FixedStack(T[] stack) { + this.stack = stack; + this.top = 0; + this.size = stack.length; + } + + public void push(T object) { + if (top == stack.length) + top = 0; + + stack[top] = object; + top++; + + if (filled < size - 1) + filled++; + } + + public T pop() { + if (filled <= 0) return null; + + if (top - 1 < 0) + top = size; + + top--; + T object = stack[top]; + filled--; + + return object; + } + + public void clear() { + top = 0; + filled = 0; + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return filled <= 0; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/helper/InventoryHelper.java b/src/main/java/nl/requios/effortlessbuilding/helper/InventoryHelper.java new file mode 100644 index 0000000..4471647 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/helper/InventoryHelper.java @@ -0,0 +1,30 @@ +package nl.requios.effortlessbuilding.helper; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; + +public class InventoryHelper { + + public static ItemStack findItemStackInInventory(EntityPlayer player, IBlockState blockState) { + for (ItemStack invStack : player.inventory.mainInventory) { + if (!invStack.isEmpty() && invStack.getItem() instanceof ItemBlock && + ((ItemBlock) invStack.getItem()).getBlock().equals(blockState.getBlock())) { + return invStack; + } + } + return ItemStack.EMPTY; + } + + public static int findTotalBlocksInInventory(EntityPlayer player, IBlockState blockState) { + int total = 0; + for (ItemStack invStack : player.inventory.mainInventory) { + if (!invStack.isEmpty() && invStack.getItem() instanceof ItemBlock && + ((ItemBlock) invStack.getItem()).getBlock().equals(blockState.getBlock())) { + total += invStack.getCount(); + } + } + return total; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java index 2869ea6..cfed703 100644 --- a/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java +++ b/src/main/java/nl/requios/effortlessbuilding/proxy/ClientProxy.java @@ -77,14 +77,15 @@ public class ClientProxy implements IProxy { @Override public void init(FMLInitializationEvent event) { // register key bindings - keyBindings = new KeyBinding[4]; + keyBindings = new KeyBinding[5]; // instantiate the key bindings keyBindings[0] = new KeyBinding("key.effortlessbuilding.hud.desc", Keyboard.KEY_ADD, "key.effortlessbuilding.category"); keyBindings[1] = new KeyBinding("key.effortlessbuilding.replace.desc", Keyboard.KEY_SUBTRACT, "key.effortlessbuilding.category"); keyBindings[2] = new KeyBinding("key.effortlessbuilding.creative.desc", Keyboard.KEY_NONE, "key.effortlessbuilding.category"); keyBindings[3] = new KeyBinding("key.effortlessbuilding.mode.desc", Keyboard.KEY_LMENU, "key.effortlessbuilding.category"); -// keyBindings[4] = new KeyBinding("Reload shaders", Keyboard.KEY_TAB, "key.effortlessbuilding.category"); + keyBindings[4] = new KeyBinding("key.effortlessbuilding.undo.desc", Keyboard.KEY_U, "key.effortlessbuilding.category"); +// keyBindings[5] = new KeyBinding("Reload shaders", Keyboard.KEY_TAB, "key.effortlessbuilding.category"); // register all the key bindings for (int i = 0; i < keyBindings.length; ++i) { @@ -318,8 +319,16 @@ public class ClientProxy implements IProxy { } } -// if (keyBindings[4].isPressed()) { -// //TODO remove + //Undo + if (keyBindings[4].isPressed()) { + ModeOptions.ActionEnum action = ModeOptions.ActionEnum.UNDO; + if (player.isSneaking()) action = ModeOptions.ActionEnum.REDO; + ModeOptions.performAction(player, action); + EffortlessBuilding.packetHandler.sendToServer(new ModeActionMessage(action)); + } + + //For shader development +// if (keyBindings[5].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 0992932..36deedf 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviewRenderer.java @@ -26,6 +26,7 @@ import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.helper.InventoryHelper; import nl.requios.effortlessbuilding.helper.ReachHelper; import nl.requios.effortlessbuilding.helper.SurvivalHelper; import nl.requios.effortlessbuilding.item.ItemRandomizerBag; @@ -158,12 +159,7 @@ public class BlockPreviewRenderer { List newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates); - Collections.sort(newCoordinates, (lhs, rhs) -> { - // -1 - less than, 1 - greater than, 0 - equal - double lhsDistanceToPlayer = new Vec3d(lhs).subtract(player.getPositionEyes(1f)).lengthSquared(); - double rhsDistanceToPlayer = new Vec3d(rhs).subtract(player.getPositionEyes(1f)).lengthSquared(); - return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer); - }); + sortOnDistanceToPlayer(newCoordinates, player); hitVec = new Vec3d(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z))); @@ -206,12 +202,14 @@ public class BlockPreviewRenderer { //Render block previews if (blockStates.size() != 0 && newCoordinates.size() == blockStates.size()) { + int blockCount; + //Use fancy shader if config allows, otherwise outlines if (BuildConfig.visuals.useShaders && newCoordinates.size() < BuildConfig.visuals.shaderTreshold) { RenderHandler.beginBlockPreviews(); - renderBlockPreviews(newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); + blockCount = renderBlockPreviews(newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking); RenderHandler.endBlockPreviews(); } else { @@ -227,8 +225,27 @@ public class BlockPreviewRenderer { } RenderHandler.endLines(); + + blockCount = newCoordinates.size(); + } + + //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); + + String dimensions = "("; + if (dim.getX() > 1) dimensions += dim.getX() + "x"; + if (dim.getZ() > 1) dimensions += dim.getZ() + "x"; + if (dim.getY() > 1) dimensions += dim.getY() + "x"; + dimensions = dimensions.substring(0, dimensions.length() - 1); + if (dimensions.length() > 1) dimensions += ")"; + + EffortlessBuilding.log(player, blockCount + " blocks " + dimensions, true); } } + + } RenderHandler.beginLines(); @@ -264,14 +281,15 @@ public class BlockPreviewRenderer { BuildConfig.visuals.alwaysShowBlockPreview; } - protected static void renderBlockPreviews(List coordinates, List blockStates, + protected static int renderBlockPreviews(List coordinates, List blockStates, List itemStacks, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean checkCanPlace, boolean red) { EntityPlayer player = Minecraft.getMinecraft().player; ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher(); + int blocksValid = 0; - if (coordinates.isEmpty()) return; + if (coordinates.isEmpty()) return blocksValid; for (int i = coordinates.size() - 1; i >= 0; i--) { BlockPos blockPos = coordinates.get(i); @@ -289,43 +307,57 @@ public class BlockPreviewRenderer { new Vec3d(blockPos), new Vec3d(firstPos), new Vec3d(secondPos), blockPos == secondPos, red)); RenderHandler.renderBlockPreview(dispatcher, blockPos, blockState); + blocksValid++; } } + return blocksValid; } public static void onBlocksPlaced() { + onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); + } + + public static void onBlocksPlaced(List coordinates, List itemStacks, List blockStates, + BlockPos firstPos, BlockPos secondPos) { EntityPlayerSP player = Minecraft.getMinecraft().player; ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); //Check if block previews are enabled - if (doRenderBlockPreviews(modifierSettings, modeSettings, previousFirstPos)) { + if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) { //Save current coordinates, blockstates and itemstacks - if (!previousCoordinates.isEmpty() && previousBlockStates.size() == previousCoordinates.size() && - previousCoordinates.size() > 1 && previousCoordinates.size() < BuildConfig.visuals.shaderTreshold) { + if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && + coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderTreshold) { - placedDataList.add(new PlacedData(ClientProxy.ticksInGame, previousCoordinates, previousBlockStates, - previousItemStacks, previousFirstPos, previousSecondPos, false)); + placedDataList.add(new PlacedData(ClientProxy.ticksInGame, coordinates, blockStates, + itemStacks, firstPos, secondPos, false)); } } } public static void onBlocksBroken() { + onBlocksBroken(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos); + } + + public static void onBlocksBroken(List coordinates, List itemStacks, List blockStates, + BlockPos firstPos, BlockPos secondPos) { EntityPlayerSP player = Minecraft.getMinecraft().player; ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); //Check if block previews are enabled - if (doRenderBlockPreviews(modifierSettings, modeSettings, previousFirstPos)) { + if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) { //Save current coordinates, blockstates and itemstacks - if (!previousCoordinates.isEmpty() && previousBlockStates.size() == previousCoordinates.size() && - previousCoordinates.size() > 1 && previousCoordinates.size() < BuildConfig.visuals.shaderTreshold) { + if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() && + coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderTreshold) { - placedDataList.add(new PlacedData(ClientProxy.ticksInGame, previousCoordinates, previousBlockStates, - previousItemStacks, previousFirstPos, previousSecondPos, true)); + sortOnDistanceToPlayer(coordinates, player); + + placedDataList.add(new PlacedData(ClientProxy.ticksInGame, coordinates, blockStates, + itemStacks, firstPos, secondPos, true)); } } @@ -373,4 +405,15 @@ public class BlockPreviewRenderer { ARBShaderObjects.glUniform1iARB(redUniform, red ? 1 : 0); }; } + + private static void sortOnDistanceToPlayer(List coordinates, EntityPlayer player) { + + Collections.sort(coordinates, (lhs, rhs) -> { + // -1 - less than, 1 - greater than, 0 - equal + double lhsDistanceToPlayer = new Vec3d(lhs).subtract(player.getPositionEyes(1f)).lengthSquared(); + double rhsDistanceToPlayer = new Vec3d(rhs).subtract(player.getPositionEyes(1f)).lengthSquared(); + return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer); + }); + + } } diff --git a/src/main/resources/assets/effortlessbuilding/lang/en_us.lang b/src/main/resources/assets/effortlessbuilding/lang/en_us.lang index 5dada4d..83a945e 100644 --- a/src/main/resources/assets/effortlessbuilding/lang/en_us.lang +++ b/src/main/resources/assets/effortlessbuilding/lang/en_us.lang @@ -3,6 +3,7 @@ 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 +key.effortlessbuilding.undo.desc=Undo item.effortlessbuilding:randomizer_bag.name=Randomizer Bag item.effortlessbuilding:reach_upgrade1.name=Reach Upgrade 1 @@ -19,8 +20,8 @@ effortlessbuilding.mode.diagonal_wall=Diagonal Wall (WIP) effortlessbuilding.mode.slope_floor=Slope Floor (WIP) effortlessbuilding.mode.cube=Cube (WIP) -effortlessbuilding.action.undo=Undo (WIP) -effortlessbuilding.action.redo=Redo (WIP) +effortlessbuilding.action.undo=Undo +effortlessbuilding.action.redo=Redo effortlessbuilding.action.replace=Replace effortlessbuilding.action.open_modifier_settings=Open Modifier Settings diff --git a/src/main/resources/assets/effortlessbuilding/textures/icons/open_modifier_settings.png b/src/main/resources/assets/effortlessbuilding/textures/icons/open_modifier_settings.png index 2d846ea5882eeb39d721bfe40f80f4249e6a8119..80c2180dd7b52e661a0439408125e3378dec69c4 100644 GIT binary patch delta 1335 zcmV-71<3ln0?7)HBYy-VdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+O3vrwxlWy zh5u(1cL*jB9>YO_wR#43_;;iDC|{?#ZWDF_z1I*RUce*ZotPz z8zo^9hUK%2JlOf%FNSsUu}<3!!IHsfmP=QBN{x}()|cW7E< z!KB-T?#Rfp&i$HVe=ga>>-2B*eXZX+Ut13`vcjJcl495lIENO+AOjOk!f0@2qq3|f zoMk?{d+lhOp2XW`C+&vR*UhICG`McyP8#)f)_d({g-+Dg$R9UQ27Wkx4%*e4L#<_b zyJQ`da@1dHeSejC2E!3jn)xw@!{e)%`}v4C0$y15YLsKw<(lgJs!r;W=)c<4qh8IWsKL3=`tY^`N`O zKRGr>wYJh_9>S&-@{(55NTZQ!XIKFU*RH9~`pyR(>VIZ9fHfefv&;c6n1*q2F)Hu1 zWoFI>b~;$<<<35L2apJ{70Ivxfx)4?+40~-JTr6*;FFNK7%L+Pu&QLH3O2?#zzNb~ z;~CGht*9)^*jfNWjWZr-&_IA?t)hOs4XB}{MlGqTQ?EgzCTE;=>YVeg?NP}aZ@qfw zy$?S66n{*xL4ylEgb+iD5)HO6jV^kMA;y>zf+o05csju%rBOyR+EGV0`Z0`gOcTmy zvXf43@>7`Nl(J-#(f-+GPdVh6Q{jSADz<2G#g|ZGNfotLO|@04tGU!VQ>cI%YlD-y z5d%S6ShVuSZYTH3jnw`YH+dpQ4c*@$M-5#kcjb0NEvNYx#msHj+TEk~wEe7u$2Jf**r&+OJgCJtS6zS3eU|xfHbM&N)AN4ok1;mr>qyIm$w>278vHT_J#oJGc-?T;qNfjq4g$Y+MZU^LFHEnE^3uBWb7$}D-};2Egx=cWPU|HL zdQ#@7+}^v-?Lyz|vNv1a#Wy_&b1lj#$y@L4z4qhp#3h*eLlFACU$2L3vn}+j$A81t zzvZ#Jsb$?E<;!GtsKvOCo^6gkoBL3R;pffZ(jvokm#A9K4)Fh4r2ha&+6+(VctB17 z000SaNLh0L01m$Z01m$aI0aKA00007bV*G`2jc?|6BZi0UHFaw006m3L_t(I%cWAw z4aFb~^J5D}Fk>)8_v6}OdO=FZ@qaso$Agw08YvRO`Pua=ZJn<}OBTZ^PhDRQf|s@5u!&Hc}0002WNklz+Wn{*NmWM^g<7!*l-g9pCE zrGbc0L@q~cYC-_mwr#&x2eS7b-g}UQ)|y&t){yFnPx3v1h%j}mp`6HCs}4s9ApiiZ zwWkK_y54NNgT2w2@YXAY5H7_($wAY$?SGS)FEPfbj4=d&VHi%y#uxxV-}gCC%{aB^>EX>4U6ba`-PAZ2)IW&i+q+SQj^mg^`C zMgJK^55bZUkKte*YxNB7@cTfBorjY+)%9~j#Sj@J`D{T_rvCZ2nSb!&#ad(42N#`N z@UqcHP8fvc^1Af2*m0d7mh0foZnhf?lc1N&M_1?k1iS7Fynmhc_1nA%r<-wZyP;mx1?#8dnwG~<^=bk(X^8uYc~{npmvRXccbbS;z_&KN@%TK zo-SDjrJR`$b$@-7F~Y1w3K~u>zVaS~e7kp>`ung%UNYqtBF) zqUFjSJ#%ImwjiD(=K;m`MvXmS6e9sVGN=j6yyZe8ZXEG41w<}{nM`m-Sn<@%k?|*O zbJSWdU7{syS|Km)HH|bnxqihg0HHQD)uHcb=B93z8-K6@f;walc)+x*7ZyRd7zNW;N~Nr?mga324> zH~-hcU*3#O^1E+;Y@lx&Q&8x z`A5=R6M{x>5_+BBvyNs@vX;=Df3-nA7}G=uN`G|cz4gr zd=%uaT8^`M=jcOLc`u2};Fj7!*?n;gn155}=&R%Mrew{1g2#Lwx5HhvV_Fyo+rduY zr(!Rye%L-{FZt)p6Tb+5wYXWb5J+?7O(mG zBz0ee-$?k6+WZHdQrslqi@?dOfN-|D#Ki0Jwx#J5kjsikaOgtl*>M2q(dt&3}Y1 if`8!0002WNklz+Wn{*NmWM^g<7!*l-g9pCE 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 delta 291 zcmV+;0o?xK3B3Z4BYyw}VoOIv0RI600RN!9r;`8x010qNS#tmY4!-~Z4!;371ydvd z000McNliru;{pf*0U|7q9LN9w0Ln>3K~y-)#Zy5JgCGzb8b8Ax{fY_Rc!xLfDR3n5 zH5$J&y%b7Sj7ghx5)Nc%W)~O~NqmC`zQm=0h)_f>M{H_B0DsuFZNFCsviBa|dys_I znp$htkm`v~@;!lwFmkwm9FXD&MUO0|pHdX02PScc)ijJ%a pGg2hW<($K^EF5sooz}}JKLO7noGN(9psN4?002ovPDHLkV1nHVc3S`d