Added buttons in radial menu for replace modes. Implemented/fixed replace modes.

Made ModeOptions clientside and UndoRedo serverside.
New config option: only show previews when building, default true.
Overhauled drawOutlinesIfNoBlockInHand, now drawOutlineAtBreakPosition.
Fixed not being able to break blocks.
This commit is contained in:
Christian Knaapen
2023-01-24 02:06:43 +01:00
parent af4ef73375
commit 4ea34fc854
31 changed files with 544 additions and 624 deletions

View File

@@ -10,7 +10,7 @@ public class ClientConfig {
public static class Visuals { public static class Visuals {
public final ForgeConfigSpec.ConfigValue<Boolean> showBlockPreviews; public final ForgeConfigSpec.ConfigValue<Boolean> showBlockPreviews;
public final ForgeConfigSpec.ConfigValue<Boolean> alwaysShowBlockPreview; public final ForgeConfigSpec.ConfigValue<Boolean> onlyShowBlockPreviewsWhenBuilding;
public final ForgeConfigSpec.ConfigValue<Integer> maxBlockPreviews; public final ForgeConfigSpec.ConfigValue<Integer> maxBlockPreviews;
public Visuals(ForgeConfigSpec.Builder builder) { public Visuals(ForgeConfigSpec.Builder builder) {
@@ -18,16 +18,16 @@ public class ClientConfig {
showBlockPreviews = builder showBlockPreviews = builder
.comment("Show previews of the blocks while placing them") .comment("Show previews of the blocks while placing them")
.define("useShaders", true); .define("showBlockPreviews", true);
alwaysShowBlockPreview = builder onlyShowBlockPreviewsWhenBuilding = builder
.comment("Show a block preview if you have a block in hand even in the 'Disabled' build mode") .comment("Show block previews only when actively using a build mode")
.define("alwaysShowBlockPreview", false); .define("onlyShowBlockPreviewsWhenBuilding", true);
maxBlockPreviews = builder maxBlockPreviews = builder
.comment("Don't show block previews when placing more than this many blocks. " + .comment("Don't show block previews when placing more than this many blocks. " +
"The outline will always be rendered.") "The outline will always be rendered.")
.define("shaderTreshold", 500); .define("maxBlockPreviews", 500);
builder.pop(); builder.pop();

View File

@@ -52,15 +52,14 @@ public class ClientEvents {
EffortlessBuilding.log("Registering KeyMappings!"); EffortlessBuilding.log("Registering KeyMappings!");
// register key bindings // register key bindings
keyBindings = new KeyMapping[6]; keyBindings = new KeyMapping[5];
// instantiate the key bindings // instantiate the key bindings
keyBindings[0] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category"); keyBindings[0] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category");
keyBindings[1] = new KeyMapping("key.effortlessbuilding.replace.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_SUBTRACT, 0), "key.effortlessbuilding.category"); keyBindings[1] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category");
keyBindings[2] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category"); keyBindings[2] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category");
keyBindings[3] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category"); keyBindings[3] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category");
keyBindings[4] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category"); keyBindings[4] = new KeyMapping("key.effortlessbuilding.altplacement.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_CONTROL, 0), "key.effortlessbuilding.category");
keyBindings[5] = new KeyMapping("key.effortlessbuilding.altplacement.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_CONTROL, 0), "key.effortlessbuilding.category");
for (KeyMapping keyBinding : keyBindings) { for (KeyMapping keyBinding : keyBindings) {
event.register(keyBinding); event.register(keyBinding);
@@ -148,19 +147,8 @@ public class ClientEvents {
if (player == null) if (player == null)
return; return;
//Remember to send packet to server if necessary
//Show Modifier Settings GUI
if (keyBindings[0].consumeClick()) {
openModifierSettings();
}
//QuickReplace toggle
if (keyBindings[1].consumeClick()) {
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
}
//Radial menu //Radial menu
if (keyBindings[2].isDown()) { if (keyBindings[0].isDown()) {
if (ReachHelper.getMaxReach(player) > 0) { if (ReachHelper.getMaxReach(player) > 0) {
if (!RadialMenu.instance.isVisible()) { if (!RadialMenu.instance.isVisible()) {
Minecraft.getInstance().setScreen(RadialMenu.instance); Minecraft.getInstance().setScreen(RadialMenu.instance);
@@ -170,22 +158,23 @@ public class ClientEvents {
} }
} }
//Show Modifier Settings GUI
if (keyBindings[1].consumeClick()) {
openModifierSettings();
}
//Undo (Ctrl+Z) //Undo (Ctrl+Z)
if (keyBindings[3].consumeClick()) { if (keyBindings[2].consumeClick()) {
ModeOptions.ActionEnum action = ModeOptions.ActionEnum.UNDO; ModeOptions.performAction(player, ModeOptions.ActionEnum.UNDO);
ModeOptions.performAction(player, action);
PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action));
} }
//Redo (Ctrl+Y) //Redo (Ctrl+Y)
if (keyBindings[4].consumeClick()) { if (keyBindings[3].consumeClick()) {
ModeOptions.ActionEnum action = ModeOptions.ActionEnum.REDO; ModeOptions.performAction(player, ModeOptions.ActionEnum.REDO);
ModeOptions.performAction(player, action);
PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action));
} }
//Change placement mode //Change placement mode
if (keyBindings[5].consumeClick()) { if (keyBindings[4].isDown()) {
//Toggle between first two actions of the first option of the current build mode //Toggle between first two actions of the first option of the current build mode
BuildModeEnum currentBuildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode(); BuildModeEnum currentBuildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
if (currentBuildMode.options.length > 0) { if (currentBuildMode.options.length > 0) {
@@ -193,10 +182,8 @@ public class ClientEvents {
if (option.actions.length >= 2) { if (option.actions.length >= 2) {
if (ModeOptions.getOptionSetting(option) == option.actions[0]) { if (ModeOptions.getOptionSetting(option) == option.actions[0]) {
ModeOptions.performAction(player, option.actions[1]); ModeOptions.performAction(player, option.actions[1]);
PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[1]));
} else { } else {
ModeOptions.performAction(player, option.actions[0]); ModeOptions.performAction(player, option.actions[0]);
PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(option.actions[0]));
} }
} }
} }

View File

@@ -74,7 +74,7 @@ public class CommonConfig {
undoStackSize = builder undoStackSize = builder
.comment("How many placements are remembered for the undo functionality.") .comment("How many placements are remembered for the undo functionality.")
.worldRestart() .worldRestart()
.define("undoStackSize", 10); .define("undoStackSize", 50);
builder.pop(); builder.pop();
} }

View File

@@ -18,13 +18,11 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; import nl.requios.effortlessbuilding.systems.UndoRedo;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager; import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.systems.ServerBuildState; import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.utilities.ReachHelper; import nl.requios.effortlessbuilding.utilities.ReachHelper;
import nl.requios.effortlessbuilding.network.AddUndoMessage;
import nl.requios.effortlessbuilding.network.ClearUndoMessage;
import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.network.PacketHandler;
@EventBusSubscriber @EventBusSubscriber
@@ -72,12 +70,6 @@ public class CommonEvents {
if (isPlayerHoldingBlock(player)) { if (isPlayerHoldingBlock(player)) {
event.setCanceled(true); event.setCanceled(true);
} }
} else {
//NORMAL mode, let vanilla handle block placing
//TODO move UndoRedo to serverside only
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState()));
} }
} }
@@ -93,16 +85,6 @@ public class CommonEvents {
if (!ServerBuildState.isLikeVanilla(player) && ReachHelper.canBreakFar(player)) { if (!ServerBuildState.isLikeVanilla(player) && ReachHelper.canBreakFar(player)) {
event.setCanceled(true); event.setCanceled(true);
} else {
//NORMAL mode, let vanilla handle block breaking
//Add to undo stack in client
//TODO move UndoRedo to serverside only
if (player instanceof ServerPlayer && event.getState() != null && event.getPos() != null) {
PacketDistributor.PacketTarget packetTarget = PacketDistributor.PLAYER.with(() -> (ServerPlayer) player);
if (packetTarget != null)
PacketHandler.INSTANCE.send(packetTarget, new AddUndoMessage(event.getPos(), event.getState(), Blocks.AIR.defaultBlockState()));
}
} }
} }
@@ -116,6 +98,7 @@ public class CommonEvents {
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof FakePlayer) return; if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity(); Player player = event.getEntity();
ServerBuildState.handleNewPlayer(player);
ModifierSettingsManager.handleNewPlayer(player); ModifierSettingsManager.handleNewPlayer(player);
} }
@@ -126,13 +109,13 @@ public class CommonEvents {
if (player.getCommandSenderWorld().isClientSide) return; if (player.getCommandSenderWorld().isClientSide) return;
UndoRedo.clear(player); UndoRedo.clear(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage());
} }
@SubscribeEvent @SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) { public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (event.getEntity() instanceof FakePlayer) return; if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity(); Player player = event.getEntity();
ServerBuildState.handleNewPlayer(player);
ModifierSettingsManager.handleNewPlayer(player); ModifierSettingsManager.handleNewPlayer(player);
} }
@@ -149,10 +132,10 @@ public class CommonEvents {
modifierSettings.getArraySettings().enabled = false; modifierSettings.getArraySettings().enabled = false;
ModifierSettingsManager.setModifierSettings(player, modifierSettings); ModifierSettingsManager.setModifierSettings(player, modifierSettings);
ServerBuildState.handleNewPlayer(player);
ModifierSettingsManager.handleNewPlayer(player); ModifierSettingsManager.handleNewPlayer(player);
UndoRedo.clear(player); UndoRedo.clear(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage());
} }
@SubscribeEvent @SubscribeEvent
@@ -165,6 +148,4 @@ public class CommonEvents {
Player newPlayer = event.getEntity(); Player newPlayer = event.getEntity();
ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer)); ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer));
} }
}
}

View File

@@ -1,11 +1,17 @@
package nl.requios.effortlessbuilding.buildmode; package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo; import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.PerformRedoPacket;
import nl.requios.effortlessbuilding.network.PerformUndoPacket;
import nl.requios.effortlessbuilding.systems.BuildSettings;
@OnlyIn(Dist.CLIENT)
public class ModeOptions { public class ModeOptions {
private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED; private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED;
@@ -58,76 +64,43 @@ public class ModeOptions {
return circleStart; return circleStart;
} }
//Called on both client and server
public static void performAction(Player player, ActionEnum action) { public static void performAction(Player player, ActionEnum action) {
if (action == null) return; if (action == null) return;
switch (action) { switch (action) {
case UNDO: case UNDO -> PacketHandler.INSTANCE.sendToServer(new PerformUndoPacket());
UndoRedo.undo(player); case REDO -> PacketHandler.INSTANCE.sendToServer(new PerformRedoPacket());
break; case OPEN_MODIFIER_SETTINGS -> ClientEvents.openModifierSettings();
case REDO: case OPEN_PLAYER_SETTINGS -> ClientEvents.openPlayerSettings();
UndoRedo.redo(player);
break;
case REPLACE:
if (player.level.isClientSide)
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
break;
case OPEN_MODIFIER_SETTINGS:
if (player.level.isClientSide)
ClientEvents.openModifierSettings();
break;
case OPEN_PLAYER_SETTINGS:
if (player.level.isClientSide)
ClientEvents.openPlayerSettings();
break;
case NORMAL_SPEED: case REPLACE_ONLY_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_AIR);
buildSpeed = ActionEnum.NORMAL_SPEED; case REPLACE_BLOCKS_AND_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.BLOCKS_AND_AIR);
break; case REPLACE_ONLY_BLOCKS -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_BLOCKS);
case FAST_SPEED: case REPLACE_FILTERED_BY_OFFHAND -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.FILTERED_BY_OFFHAND);
buildSpeed = ActionEnum.FAST_SPEED; case TOGGLE_PROTECT_TILE_ENTITIES -> EffortlessBuildingClient.BUILD_SETTINGS.toggleProtectTileEntities();
break;
case FULL: case NORMAL_SPEED -> buildSpeed = ActionEnum.NORMAL_SPEED;
fill = ActionEnum.FULL; case FAST_SPEED -> buildSpeed = ActionEnum.FAST_SPEED;
break;
case HOLLOW: case FULL -> fill = ActionEnum.FULL;
fill = ActionEnum.HOLLOW; case HOLLOW -> fill = ActionEnum.HOLLOW;
break;
case CUBE_FULL: case CUBE_FULL -> cubeFill = ActionEnum.CUBE_FULL;
cubeFill = ActionEnum.CUBE_FULL; case CUBE_HOLLOW -> cubeFill = ActionEnum.CUBE_HOLLOW;
break; case CUBE_SKELETON -> cubeFill = ActionEnum.CUBE_SKELETON;
case CUBE_HOLLOW:
cubeFill = ActionEnum.CUBE_HOLLOW; case SHORT_EDGE -> raisedEdge = ActionEnum.SHORT_EDGE;
break; case LONG_EDGE -> raisedEdge = ActionEnum.LONG_EDGE;
case CUBE_SKELETON:
cubeFill = ActionEnum.CUBE_SKELETON; case THICKNESS_1 -> lineThickness = ActionEnum.THICKNESS_1;
break; case THICKNESS_3 -> lineThickness = ActionEnum.THICKNESS_3;
case SHORT_EDGE: case THICKNESS_5 -> lineThickness = ActionEnum.THICKNESS_5;
raisedEdge = ActionEnum.SHORT_EDGE;
break; case CIRCLE_START_CENTER -> circleStart = ActionEnum.CIRCLE_START_CENTER;
case LONG_EDGE: case CIRCLE_START_CORNER -> circleStart = ActionEnum.CIRCLE_START_CORNER;
raisedEdge = ActionEnum.LONG_EDGE;
break;
case THICKNESS_1:
lineThickness = ActionEnum.THICKNESS_1;
break;
case THICKNESS_3:
lineThickness = ActionEnum.THICKNESS_3;
break;
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.level.isClientSide && if (player.level.isClientSide &&
action != ActionEnum.REPLACE &&
action != ActionEnum.OPEN_MODIFIER_SETTINGS && action != ActionEnum.OPEN_MODIFIER_SETTINGS &&
action != ActionEnum.OPEN_PLAYER_SETTINGS) { action != ActionEnum.OPEN_PLAYER_SETTINGS) {
@@ -138,10 +111,15 @@ public class ModeOptions {
public enum ActionEnum { public enum ActionEnum {
UNDO("effortlessbuilding.action.undo"), UNDO("effortlessbuilding.action.undo"),
REDO("effortlessbuilding.action.redo"), REDO("effortlessbuilding.action.redo"),
REPLACE("effortlessbuilding.action.replace"),
OPEN_MODIFIER_SETTINGS("effortlessbuilding.action.open_modifier_settings"), OPEN_MODIFIER_SETTINGS("effortlessbuilding.action.open_modifier_settings"),
OPEN_PLAYER_SETTINGS("effortlessbuilding.action.open_player_settings"), OPEN_PLAYER_SETTINGS("effortlessbuilding.action.open_player_settings"),
REPLACE_ONLY_AIR("effortlessbuilding.action.replace_only_air"),
REPLACE_BLOCKS_AND_AIR("effortlessbuilding.action.replace_blocks_and_air"),
REPLACE_ONLY_BLOCKS("effortlessbuilding.action.replace_only_blocks"),
REPLACE_FILTERED_BY_OFFHAND("effortlessbuilding.action.replace_filtered_by_offhand"),
TOGGLE_PROTECT_TILE_ENTITIES("effortlessbuilding.action.toggle_protect_tile_entities"),
NORMAL_SPEED("effortlessbuilding.action.normal_speed"), NORMAL_SPEED("effortlessbuilding.action.normal_speed"),
FAST_SPEED("effortlessbuilding.action.fast_speed"), FAST_SPEED("effortlessbuilding.action.fast_speed"),

View File

@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet; import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper; import nl.requios.effortlessbuilding.utilities.ReachHelper;
@@ -29,18 +30,14 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
if (clicks == 1) { if (clicks == 1) {
//First click, remember starting position //First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again //If clicking in air, reset and try again
if (blocks.size() == 0) { if (firstBlockEntry == null) clicks = 0;
clicks = 0;
return false;
}
firstBlockEntry = blocks.getFirstBlockEntry();
} else if (clicks == 2) { } else if (clicks == 2) {
//Second click, find second position //Second click, find second position
//If clicking in air, reset and try again
if (blocks.size() == 0) { if (blocks.size() == 0) {
clicks = 0; clicks = 0;
return false; return false;

View File

@@ -3,6 +3,7 @@ package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet; import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper; import nl.requios.effortlessbuilding.utilities.ReachHelper;
@@ -25,14 +26,11 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
if (clicks == 1) { if (clicks == 1) {
//First click, remember starting position //First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again //If clicking in air, reset and try again
if (blocks.size() == 0) { if (firstBlockEntry == null) clicks = 0;
clicks = 0;
return false;
}
firstBlockEntry = blocks.getFirstBlockEntry();
} else { } else {
//Second click, place blocks //Second click, place blocks
clicks = 0; clicks = 0;

View File

@@ -19,10 +19,11 @@ import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.systems.DelayedBlockPlacer; import nl.requios.effortlessbuilding.systems.DelayedBlockPlacer;
import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.systems.UndoRedo;
import nl.requios.effortlessbuilding.utilities.BlockSet; import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper; import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;

View File

@@ -14,6 +14,7 @@ import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
@@ -22,8 +23,6 @@ import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.network.ModeActionMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
import org.apache.commons.lang3.text.WordUtils; import org.apache.commons.lang3.text.WordUtils;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -64,7 +63,8 @@ public class RadialMenu extends Screen {
private final double textDistance = 75; private final double textDistance = 75;
private final double buttonDistance = 105; private final double buttonDistance = 105;
private final float fadeSpeed = 0.3f; private final float fadeSpeed = 0.3f;
private final int descriptionHeight = 100; private final int buildModeDescriptionHeight = 100;
private final int actionDescriptionWidth = 200;
public BuildModeEnum switchTo = null; public BuildModeEnum switchTo = null;
public ActionEnum doAction = null; public ActionEnum doAction = null;
@@ -91,7 +91,7 @@ public class RadialMenu extends Screen {
public void tick() { public void tick() {
super.tick(); super.tick();
if (!ClientEvents.isKeybindDown(2)) { if (!ClientEvents.isKeybindDown(0)) {
onClose(); onClose();
} }
} }
@@ -146,18 +146,23 @@ public class RadialMenu extends Screen {
} }
//Add actions //Add actions
buttons.add(new MenuButton(ActionEnum.UNDO.name, ActionEnum.UNDO, -buttonDistance - 26, -13, Direction.UP)); // buttons.add(new MenuButton(ActionEnum.OPEN_PLAYER_SETTINGS, -buttonDistance - 65, -13, Direction.UP));
buttons.add(new MenuButton(ActionEnum.REDO.name, ActionEnum.REDO, -buttonDistance, -13, Direction.UP)); buttons.add(new MenuButton(ActionEnum.TOGGLE_PROTECT_TILE_ENTITIES, -buttonDistance - 78, -13, Direction.UP));
// buttons.add(new MenuButton(ActionEnum.OPEN_PLAYER_SETTINGS.name, ActionEnum.OPEN_PLAYER_SETTINGS, -buttonDistance - 26 - 13, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 52, -13, Direction.UP));
buttons.add(new MenuButton(ActionEnum.OPEN_MODIFIER_SETTINGS.name, ActionEnum.OPEN_MODIFIER_SETTINGS, -buttonDistance - 26, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.UNDO, -buttonDistance - 26, -13, Direction.UP));
buttons.add(new MenuButton(ActionEnum.REPLACE.name, ActionEnum.REPLACE, -buttonDistance, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.REDO, -buttonDistance, -13, Direction.UP));
buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_AIR, -buttonDistance - 78, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_BLOCKS_AND_AIR, -buttonDistance - 52, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_BLOCKS, -buttonDistance - 26, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_FILTERED_BY_OFFHAND, -buttonDistance, 13, Direction.DOWN));
//Add buildmode dependent options //Add buildmode dependent options
OptionEnum[] options = currentBuildMode.options; OptionEnum[] options = currentBuildMode.options;
for (int i = 0; i < options.length; i++) { for (int i = 0; i < options.length; i++) {
for (int j = 0; j < options[i].actions.length; j++) { for (int j = 0; j < options[i].actions.length; j++) {
ActionEnum action = options[i].actions[j]; ActionEnum action = options[i].actions[j];
buttons.add(new MenuButton(action.name, action, buttonDistance + j * 26, -13 + i * 39, Direction.DOWN)); buttons.add(new MenuButton(action, buttonDistance + j * 26, -13 + i * 39, Direction.DOWN));
} }
} }
@@ -250,15 +255,17 @@ public class RadialMenu extends Screen {
private void drawSideButtonBackgrounds(BufferBuilder buffer, double middleX, double middleY, double mouseXCenter, double mouseYCenter, ArrayList<MenuButton> buttons) { private void drawSideButtonBackgrounds(BufferBuilder buffer, double middleX, double middleY, double mouseXCenter, double mouseYCenter, ArrayList<MenuButton> buttons) {
for (final MenuButton btn : buttons) { for (final MenuButton btn : buttons) {
final boolean isSelected = final boolean isHighlighted = btn.x1 <= mouseXCenter && btn.x2 >= mouseXCenter && btn.y1 <= mouseYCenter && btn.y2 >= mouseYCenter;
boolean isSelected =
btn.action == getBuildSpeed() || btn.action == getBuildSpeed() ||
btn.action == getFill() || btn.action == getFill() ||
btn.action == getCubeFill() || btn.action == getCubeFill() ||
btn.action == getRaisedEdge() || btn.action == getRaisedEdge() ||
btn.action == getLineThickness() || btn.action == getLineThickness() ||
btn.action == getCircleStart(); btn.action == getCircleStart() ||
btn.action == EffortlessBuildingClient.BUILD_SETTINGS.getReplaceModeActionEnum() ||
final boolean isHighlighted = btn.x1 <= mouseXCenter && btn.x2 >= mouseXCenter && btn.y1 <= mouseYCenter && btn.y2 >= mouseYCenter; btn.action == ActionEnum.TOGGLE_PROTECT_TILE_ENTITIES && EffortlessBuildingClient.BUILD_SETTINGS.shouldProtectTileEntities();
Vector4f color = sideButtonColor; Vector4f color = sideButtonColor;
if (isSelected) color = selectedColor; if (isSelected) color = selectedColor;
@@ -340,20 +347,21 @@ public class RadialMenu extends Screen {
//Draw description //Draw description
text = I18n.get(menuRegion.mode.getDescriptionKey()); text = I18n.get(menuRegion.mode.getDescriptionKey());
font.drawShadow(ms, text, (int) middleX - font.width(text) / 2f, (int) middleY + descriptionHeight, descriptionTextColor); font.drawShadow(ms, text, (int) middleX - font.width(text) / 2f, (int) middleY + buildModeDescriptionHeight, descriptionTextColor);
} }
} }
//Draw action text //Draw action text
for (final MenuButton button : buttons) { for (final MenuButton button : buttons) {
if (button.highlighted) { if (button.highlighted) {
String text = ChatFormatting.AQUA + button.name; String text = ChatFormatting.AQUA + button.name;
String description = ChatFormatting.WHITE + button.description;
//Add keybind in brackets //Add keybind in brackets
String keybind = findKeybind(button, currentBuildMode); String keybind = findKeybind(button, currentBuildMode);
String keybindFormatted = ""; boolean hasKeybind = !keybind.isEmpty();
if (!keybind.isEmpty()) keybind = ChatFormatting.GRAY + "(" + WordUtils.capitalizeFully(keybind) + ")";
keybindFormatted = ChatFormatting.GRAY + "(" + WordUtils.capitalizeFully(keybind) + ")";
if (button.textSide == Direction.WEST) { if (button.textSide == Direction.WEST) {
@@ -367,19 +375,31 @@ public class RadialMenu extends Screen {
} else if (button.textSide == Direction.UP || button.textSide == Direction.NORTH) { } else if (button.textSide == Direction.UP || button.textSide == Direction.NORTH) {
font.draw(ms, keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybindFormatted) * 0.5), int y = (int) (middleY + button.y1 - 14);
(int) (middleY + button.y1 - 26), whiteTextColor); font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), y, whiteTextColor);
font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), y -= 12;
(int) (middleY + button.y1 - 14), whiteTextColor); if (hasKeybind) {
font.draw(ms, keybind, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybind) * 0.5), y, whiteTextColor);
y -= 12;
}
if (!description.isEmpty())
font.drawWordWrap(FormattedText.of(description), (int) (middleX + (button.x1 + button.x2) * 0.5 - actionDescriptionWidth * 0.5), y, actionDescriptionWidth, whiteTextColor);
} else if (button.textSide == Direction.DOWN || button.textSide == Direction.SOUTH) { } else if (button.textSide == Direction.DOWN || button.textSide == Direction.SOUTH) {
font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), int y = (int) (middleY + button.y1 + 26);
(int) (middleY + button.y1 + 26), whiteTextColor); font.draw(ms, text, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(text) * 0.5), y, whiteTextColor);
font.draw(ms, keybindFormatted, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybindFormatted) * 0.5), y += 12;
(int) (middleY + button.y1 + 38), whiteTextColor); if (hasKeybind) {
font.draw(ms, keybind, (int) (middleX + (button.x1 + button.x2) * 0.5 - font.width(keybind) * 0.5), y, whiteTextColor);
y += 12;
}
if (!description.isEmpty())
font.drawWordWrap(FormattedText.of(description), (int) (middleX + (button.x1 + button.x2) * 0.5 - actionDescriptionWidth * 0.5), y, actionDescriptionWidth, whiteTextColor);
} }
@@ -390,15 +410,14 @@ public class RadialMenu extends Screen {
private String findKeybind(MenuButton button, BuildModeEnum currentBuildMode){ private String findKeybind(MenuButton button, BuildModeEnum currentBuildMode){
String result = ""; String result = "";
int keybindingIndex = -1; int keybindingIndex = -1;
if (button.action == ActionEnum.UNDO) keybindingIndex = 3; if (button.action == ActionEnum.OPEN_MODIFIER_SETTINGS) keybindingIndex = 1;
if (button.action == ActionEnum.REDO) keybindingIndex = 4; if (button.action == ActionEnum.UNDO) keybindingIndex = 2;
if (button.action == ActionEnum.REPLACE) keybindingIndex = 1; if (button.action == ActionEnum.REDO) keybindingIndex = 3;
if (button.action == ActionEnum.OPEN_MODIFIER_SETTINGS) keybindingIndex = 0;
if (keybindingIndex != -1) { if (keybindingIndex != -1) {
KeyMapping keyMap = ClientEvents.keyBindings[keybindingIndex]; KeyMapping keyMap = ClientEvents.keyBindings[keybindingIndex];
if (!keyMap.getKeyModifier().name().equals("none")) { if (!keyMap.getKeyModifier().name().equals("None")) {
result = keyMap.getKeyModifier().name() + " "; result = keyMap.getKeyModifier().name() + " ";
} }
result += I18n.get(keyMap.getKey().getName()); result += I18n.get(keyMap.getKey().getName());
@@ -408,10 +427,12 @@ public class RadialMenu extends Screen {
//Add (ctrl) to first two actions of first option //Add (ctrl) to first two actions of first option
if (button.action == currentBuildMode.options[0].actions[0] if (button.action == currentBuildMode.options[0].actions[0]
|| button.action == currentBuildMode.options[0].actions[1]) { || button.action == currentBuildMode.options[0].actions[1]) {
result = I18n.get(ClientEvents.keyBindings[5].getKey().getName()); result = I18n.get(ClientEvents.keyBindings[4].getKey().getDisplayName().getString());
if (result.equals("Left Control")) result = "Ctrl"; if (result.equals("Left Control")) result = "Ctrl";
} }
} }
result = result.replace("Key.keyboard.", "");
return result; return result;
} }
@@ -465,7 +486,6 @@ public class RadialMenu extends Screen {
playRadialMenuSound(); playRadialMenuSound();
ModeOptions.performAction(player, action); ModeOptions.performAction(player, action);
PacketHandler.INSTANCE.sendToServer(new ModeActionMessage(action));
if (fromMouseClick) performedActionUsingMouse = true; if (fromMouseClick) performedActionUsingMouse = true;
} }
@@ -487,11 +507,17 @@ public class RadialMenu extends Screen {
public double y1, y2; public double y1, y2;
public boolean highlighted; public boolean highlighted;
public String name; public String name;
public String description = "";
public Direction textSide; public Direction textSide;
public MenuButton(final String name, final ActionEnum action, final double x, final double y, public MenuButton(final ActionEnum action, final double x, final double y,
final Direction textSide) { final Direction textSide) {
this.name = I18n.get(name); this.name = I18n.get(action.name);
if (I18n.exists(action.name + ".description")) {
this.description = I18n.get(action.name + ".description");
}
this.action = action; this.action = action;
x1 = x - 10; x1 = x - 10;
x2 = x + 10; x2 = x + 10;

View File

@@ -88,7 +88,7 @@ public class ModifierSettingsGui extends Screen {
@Override @Override
public boolean keyPressed(int keyCode, int p_96553_, int p_96554_) { public boolean keyPressed(int keyCode, int p_96553_, int p_96554_) {
if (keyCode == ClientEvents.keyBindings[0].getKey().getValue()) { if (keyCode == ClientEvents.keyBindings[1].getKey().getValue()) {
minecraft.player.closeContainer(); minecraft.player.closeContainer();
return true; return true;
} }

View File

@@ -1,99 +0,0 @@
package nl.requios.effortlessbuilding.network;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.core.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import java.util.ArrayList;
import java.util.function.Supplier;
/***
* Sends a message to the client asking to add a block to the undo stack.
*/
@Deprecated
public class AddUndoMessage {
private final BlockPos coordinate;
private final BlockState previousBlockState;
private final BlockState newBlockState;
public AddUndoMessage() {
coordinate = BlockPos.ZERO;
previousBlockState = null;
newBlockState = null;
}
public AddUndoMessage(BlockPos coordinate, BlockState previousBlockState, BlockState newBlockState) {
this.coordinate = coordinate;
this.previousBlockState = previousBlockState;
this.newBlockState = newBlockState;
}
public static void encode(AddUndoMessage message, FriendlyByteBuf buf) {
buf.writeInt(message.coordinate.getX());
buf.writeInt(message.coordinate.getY());
buf.writeInt(message.coordinate.getZ());
buf.writeInt(Block.getId(message.previousBlockState));
buf.writeInt(Block.getId(message.newBlockState));
}
public static AddUndoMessage decode(FriendlyByteBuf buf) {
BlockPos coordinate = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt());
BlockState previousBlockState = Block.stateById(buf.readInt());
BlockState newBlockState = Block.stateById(buf.readInt());
return new AddUndoMessage(coordinate, previousBlockState, newBlockState);
}
public BlockPos getCoordinate() {
return coordinate;
}
public BlockState getPreviousBlockState() {
return previousBlockState;
}
public BlockState getNewBlockState() {
return newBlockState;
}
public static class Handler {
public static void handle(AddUndoMessage message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) {
//Received clientside
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientHandler.handle(message, ctx));
}
});
ctx.get().setPacketHandled(true);
}
}
@OnlyIn(Dist.CLIENT)
public static class ClientHandler {
public static void handle(AddUndoMessage message, Supplier<NetworkEvent.Context> ctx) {
Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx);
//Add to undo stack clientside
//Only the appropriate player that needs to add this to the undo stack gets this message
UndoRedo.addUndo(player, new UndoRedoBlockSet(
new ArrayList<BlockPos>() {{
add(message.getCoordinate());
}},
new ArrayList<BlockState>() {{
add(message.getPreviousBlockState());
}},
new ArrayList<BlockState>() {{
add(message.getNewBlockState());
}},
message.getCoordinate(), message.getCoordinate()));
}
}
}

View File

@@ -1,53 +0,0 @@
package nl.requios.effortlessbuilding.network;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import java.util.function.Supplier;
/***
* Sends a message to the client asking to clear the undo and redo stacks.
*/
@Deprecated
public class ClearUndoMessage {
public ClearUndoMessage() {
}
public static void encode(ClearUndoMessage message, FriendlyByteBuf buf) {
}
public static ClearUndoMessage decode(FriendlyByteBuf buf) {
return new ClearUndoMessage();
}
public static class Handler {
public static void handle(ClearUndoMessage message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) {
//Received clientside
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientHandler.handle(message, ctx));
}
});
ctx.get().setPacketHandled(true);
}
}
@OnlyIn(Dist.CLIENT)
public static class ClientHandler {
public static void handle(ClearUndoMessage message, Supplier<NetworkEvent.Context> ctx) {
Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx);
//Add to undo stack clientside
UndoRedo.clear(player);
}
}
}

View File

@@ -1,45 +0,0 @@
package nl.requios.effortlessbuilding.network;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import java.util.function.Supplier;
/**
* Shares mode settings (see ModeSettingsManager) between server and client
*/
@Deprecated
public class ModeActionMessage {
private ModeOptions.ActionEnum action;
public ModeActionMessage() {
}
public ModeActionMessage(ModeOptions.ActionEnum action) {
this.action = action;
}
public static void encode(ModeActionMessage message, FriendlyByteBuf buf) {
buf.writeInt(message.action.ordinal());
}
public static ModeActionMessage decode(FriendlyByteBuf buf) {
ModeOptions.ActionEnum action = ModeOptions.ActionEnum.values()[buf.readInt()];
return new ModeActionMessage(action);
}
public static class Handler {
public static void handle(ModeActionMessage message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx);
ModeOptions.performAction(player, message.action);
});
ctx.get().setPacketHandled(true);
}
}
}

View File

@@ -26,16 +26,14 @@ public class PacketHandler {
IsQuickReplacingPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER)); IsQuickReplacingPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER));
INSTANCE.registerMessage(id++, ModifierSettingsMessage.class, ModifierSettingsMessage::encode, ModifierSettingsMessage::decode, INSTANCE.registerMessage(id++, ModifierSettingsMessage.class, ModifierSettingsMessage::encode, ModifierSettingsMessage::decode,
ModifierSettingsMessage.Handler::handle); ModifierSettingsMessage.Handler::handle);
INSTANCE.registerMessage(id++, ModeActionMessage.class, ModeActionMessage::encode, ModeActionMessage::decode,
ModeActionMessage.Handler::handle);
INSTANCE.registerMessage(id++, ServerPlaceBlocksPacket.class, ServerPlaceBlocksPacket::encode, ServerPlaceBlocksPacket::decode, INSTANCE.registerMessage(id++, ServerPlaceBlocksPacket.class, ServerPlaceBlocksPacket::encode, ServerPlaceBlocksPacket::decode,
ServerPlaceBlocksPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER)); ServerPlaceBlocksPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER));
INSTANCE.registerMessage(id++, ServerBreakBlocksPacket.class, ServerBreakBlocksPacket::encode, ServerBreakBlocksPacket::decode, INSTANCE.registerMessage(id++, ServerBreakBlocksPacket.class, ServerBreakBlocksPacket::encode, ServerBreakBlocksPacket::decode,
ServerBreakBlocksPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER)); ServerBreakBlocksPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER));
INSTANCE.registerMessage(id++, AddUndoMessage.class, AddUndoMessage::encode, AddUndoMessage::decode, INSTANCE.registerMessage(id++, PerformUndoPacket.class, PerformUndoPacket::encode, PerformUndoPacket::decode,
AddUndoMessage.Handler::handle); PerformUndoPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER));
INSTANCE.registerMessage(id++, ClearUndoMessage.class, ClearUndoMessage::encode, ClearUndoMessage::decode, INSTANCE.registerMessage(id++, PerformRedoPacket.class, PerformRedoPacket::encode, PerformRedoPacket::decode,
ClearUndoMessage.Handler::handle); PerformRedoPacket.Handler::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER));
} }
} }

View File

@@ -0,0 +1,27 @@
package nl.requios.effortlessbuilding.network;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.systems.UndoRedo;
import java.util.function.Supplier;
public class PerformRedoPacket {
public PerformRedoPacket() {}
public static void encode(PerformRedoPacket message, FriendlyByteBuf buf) {}
public static PerformRedoPacket decode(FriendlyByteBuf buf) {
return new PerformRedoPacket();
}
public static class Handler {
public static void handle(PerformRedoPacket message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
UndoRedo.redo(ctx.get().getSender());
});
ctx.get().setPacketHandled(true);
}
}
}

View File

@@ -0,0 +1,27 @@
package nl.requios.effortlessbuilding.network;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.systems.UndoRedo;
import java.util.function.Supplier;
public class PerformUndoPacket {
public PerformUndoPacket() {}
public static void encode(PerformUndoPacket message, FriendlyByteBuf buf) {}
public static PerformUndoPacket decode(FriendlyByteBuf buf) {
return new PerformUndoPacket();
}
public static class Handler {
public static void handle(PerformUndoPacket message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {
UndoRedo.undo(ctx.get().getSender());
});
ctx.get().setPacketHandled(true);
}
}
}

View File

@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
@@ -29,12 +30,12 @@ public class BlockPreviews {
public void onTick() { public void onTick() {
var player = Minecraft.getInstance().player; var player = Minecraft.getInstance().player;
drawPlacedBlocks(player); drawPlacedBlocks();
drawLookAtPreview(player); drawLookAtPreview(player);
drawOutlinesIfNoBlockInHand(player); drawOutlineAtBreakPosition(player);
} }
public void drawPlacedBlocks(Player player) { public void drawPlacedBlocks() {
//Render placed blocks with appear animation //Render placed blocks with appear animation
if (ClientConfig.visuals.showBlockPreviews.get()) { if (ClientConfig.visuals.showBlockPreviews.get()) {
for (PlacedBlocksEntry placed : placedBlocksList) { for (PlacedBlocksEntry placed : placedBlocksList) {
@@ -55,21 +56,22 @@ public class BlockPreviews {
} }
public void drawLookAtPreview(Player player) { public void drawLookAtPreview(Player player) {
if (EffortlessBuildingClient.BUILD_MODES.getBuildMode() == BuildModeEnum.DISABLED && if (EffortlessBuildingClient.BUILD_MODES.getBuildMode() == BuildModeEnum.DISABLED) return;
!ClientConfig.visuals.alwaysShowBlockPreview.get()) return; if (EffortlessBuildingClient.BUILDER_CHAIN.getBuildingState() == BuilderChain.BuildingState.IDLE &&
ClientConfig.visuals.onlyShowBlockPreviewsWhenBuilding.get()) return;
var blocks = EffortlessBuildingClient.BUILDER_CHAIN.getBlocks(); var blocks = EffortlessBuildingClient.BUILDER_CHAIN.getBlocks();
if (blocks.size() == 0) return; if (blocks.size() == 0) return;
var coordinates = blocks.getCoordinates(); var coordinates = blocks.getCoordinates();
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState(); var state = EffortlessBuildingClient.BUILDER_CHAIN.getPretendBuildingState();
//Dont fade out the outline if we are still determining where to place //Dont fade out the outline if we are still determining where to place
//Every outline with same ID will not fade out (because it gets replaced) //Every outline with same ID will not fade out (because it gets replaced)
Object outlineID = "single"; Object outlineID = "single";
if (blocks.size() > 1) outlineID = blocks.firstPos; if (blocks.size() > 1) outlineID = blocks.firstPos;
if (state != BuilderChain.State.BREAKING) { if (state != BuilderChain.BuildingState.BREAKING) {
//Use fancy shader if config allows, otherwise outlines //Use fancy shader if config allows, otherwise outlines
if (ClientConfig.visuals.showBlockPreviews.get() && blocks.size() < ClientConfig.visuals.maxBlockPreviews.get()) { if (ClientConfig.visuals.showBlockPreviews.get() && blocks.size() < ClientConfig.visuals.maxBlockPreviews.get()) {
renderBlockPreviews(blocks, false, 0f); renderBlockPreviews(blocks, false, 0f);
@@ -98,7 +100,7 @@ public class BlockPreviews {
} }
//Display block count and dimensions in actionbar //Display block count and dimensions in actionbar
if (state != BuilderChain.State.IDLE) { if (state != BuilderChain.BuildingState.IDLE) {
//Find min and max values (not simply firstPos and secondPos because that doesn't work with circles) //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 minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE;
@@ -126,31 +128,35 @@ public class BlockPreviews {
} }
} }
public void drawOutlinesIfNoBlockInHand(Player player) { public void drawOutlineAtBreakPosition(Player player) {
var builderChain = EffortlessBuildingClient.BUILDER_CHAIN; if (EffortlessBuildingClient.BUILD_MODES.getBuildMode() == BuildModeEnum.DISABLED) return;
if (builderChain.isBlockInHand()) return;
if (builderChain.getState() != BuilderChain.State.IDLE) return;
var blocks = new ArrayList<>(builderChain.getBlocks().values()); BuilderChain builderChain = EffortlessBuildingClient.BUILDER_CHAIN;
if (blocks.size() == 0) return; BlockPos pos = builderChain.getStartPosForBreaking();
if (pos == null) return;
//Only render first outline if further than normal reach var abilitiesState = builderChain.getAbilitiesState();
var lookingAtNear = Minecraft.getInstance().hitResult; if (ClientConfig.visuals.onlyShowBlockPreviewsWhenBuilding.get()) {
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK) if (abilitiesState == BuilderChain.AbilitiesState.NONE) return;
blocks.remove(0); } else {
if (abilitiesState != BuilderChain.AbilitiesState.CAN_BREAK) return;
//Only render outlines if there is a block, like vanilla
blocks.removeIf(blockEntry -> blockEntry.existingBlockState == null ||
blockEntry.existingBlockState.isAir() ||
blockEntry.existingBlockState.getMaterial().isLiquid());
if (!blocks.isEmpty()) {
var coordinates = blocks.stream().map(blockEntry -> blockEntry.blockPos).collect(Collectors.toList());
CreateClient.OUTLINER.showCluster("break", coordinates)
.disableNormals()
.lineWidth(1 / 64f)
.colored(0x222222);
} }
//Only render if further than normal reach
if (EffortlessBuildingClient.BUILDER_CHAIN.getLookingAtNear() != null) return;
AABB aabb = new AABB(pos);
if (player.level.isLoaded(pos)) {
var blockState = player.level.getBlockState(pos);
if (!blockState.isAir()) {
aabb = blockState.getShape(player.level, pos).bounds().move(pos);
}
}
CreateClient.OUTLINER.showAABB("break", aabb)
.disableNormals()
.lineWidth(1 / 64f)
.colored(0x222222);
} }
protected void renderBlockPreviews(BlockSet blocks, boolean breaking, float dissolve) { protected void renderBlockPreviews(BlockSet blocks, boolean breaking, float dissolve) {
@@ -186,8 +192,8 @@ public class BlockPreviews {
t = Mth.clamp(t, 0, 1); t = Mth.clamp(t, 0, 1);
//Now we got a usable t value for this block //Now we got a usable t value for this block
t = (float) Mth.smoothstep(t); // t = (float) Mth.smoothstep(t);
// t = (float) bezier(t, .58,-0.08,.23,1.33); t = gain(t, 0.5f);
if (!breaking) { if (!breaking) {
scale = 0.5f + (t * 0.3f); scale = 0.5f + (t * 0.3f);
@@ -206,21 +212,12 @@ public class BlockPreviews {
.scale(scale); .scale(scale);
} }
//A bezier easing function where implicit first and last control points are (0,0) and (1,1). //k=1 is the identity curve, k<1 produces the classic gain() shape, and k>1 produces "s" shaped curves. The curves are symmetric (and inverse) for k=a and k=1/a.
public double bezier(double t, double x1, double y1, double x2, double y2) { //https://iquilezles.org/articles/functions/
double t2 = t * t; private float gain(float x, float k)
double t3 = t2 * t; {
float a = (float) (0.5 * Math.pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k));
double cx = 3.0 * x1; return (x < 0.5) ? a : (1.0f - a);
double bx = 3.0 * (x2 - x1) - cx;
double ax = 1.0 - cx -bx;
double cy = 3.0 * y1;
double by = 3.0 * (y2 - y1) - cy;
double ay = 1.0 - cy - by;
// Calculate the curve point at parameter value t
return (ay * t3) + (by * t2) + (cy * t) + 0;
} }
public void onBlocksPlaced(BlockSet blocks) { public void onBlocksPlaced(BlockSet blocks) {

View File

@@ -55,7 +55,7 @@ public class RenderHandler {
renderSubText(event.getPoseStack()); renderSubText(event.getPoseStack());
} }
private static final ChatFormatting highlightColor = ChatFormatting.BLUE; private static final ChatFormatting highlightColor = ChatFormatting.DARK_AQUA;
private static final ChatFormatting normalColor = ChatFormatting.WHITE; private static final ChatFormatting normalColor = ChatFormatting.WHITE;
private static final Component placingText = Component.literal( private static final Component placingText = Component.literal(
normalColor + "Left-click to " + highlightColor + "cancel, " + normalColor + "Left-click to " + highlightColor + "cancel, " +
@@ -66,10 +66,10 @@ public class RenderHandler {
normalColor + "Right-click to " + highlightColor + "cancel"); normalColor + "Right-click to " + highlightColor + "cancel");
private static void renderSubText(PoseStack ms) { private static void renderSubText(PoseStack ms) {
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState(); var state = EffortlessBuildingClient.BUILDER_CHAIN.getBuildingState();
if (state == BuilderChain.State.IDLE) return; if (state == BuilderChain.BuildingState.IDLE) return;
var text = state == BuilderChain.State.PLACING ? placingText : breakingText; var text = state == BuilderChain.BuildingState.PLACING ? placingText : breakingText;
int screenWidth = Minecraft.getInstance().getWindow().getGuiScaledWidth(); int screenWidth = Minecraft.getInstance().getWindow().getGuiScaledWidth();
int screenHeight = Minecraft.getInstance().getWindow().getGuiScaledHeight(); int screenHeight = Minecraft.getInstance().getWindow().getGuiScaledHeight();

View File

@@ -1,10 +1,8 @@
package nl.requios.effortlessbuilding.systems; package nl.requios.effortlessbuilding.systems;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket; import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket;
import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.network.PacketHandler;
@@ -12,56 +10,57 @@ import nl.requios.effortlessbuilding.network.PacketHandler;
public class BuildSettings { public class BuildSettings {
public enum ReplaceMode { public enum ReplaceMode {
ONLY_AIR, ONLY_AIR,
SOLID_AND_AIR, BLOCKS_AND_AIR,
SOLID_ONLY, ONLY_BLOCKS,
FILTERED_BY_OFFHAND FILTERED_BY_OFFHAND
} }
private boolean quickReplace = false; private ReplaceMode replaceMode = ReplaceMode.ONLY_AIR;
public ReplaceMode replaceMode = ReplaceMode.ONLY_AIR; private boolean protectTileEntities = true;
private boolean replaceTileEntities;
public boolean isQuickReplacing() { public boolean isQuickReplacing() {
return quickReplace; return replaceMode != ReplaceMode.ONLY_AIR;
}
public void toggleQuickReplace() {
setQuickReplace(!quickReplace);
}
public void setQuickReplace(boolean quickReplace) {
this.quickReplace = quickReplace;
EffortlessBuilding.log(Minecraft.getInstance().player, "Set " + ChatFormatting.GOLD + "Quick Replace " +
ChatFormatting.RESET + (this.quickReplace ? "on" : "off"));
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(this.quickReplace));
} }
public void setReplaceMode(ReplaceMode replaceMode) { public void setReplaceMode(ReplaceMode replaceMode) {
this.replaceMode = replaceMode; this.replaceMode = replaceMode;
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(isQuickReplacing()));
} }
public void setReplaceTileEntities(boolean replaceTileEntities) { public ReplaceMode getReplaceMode() {
this.replaceTileEntities = replaceTileEntities; return replaceMode;
}
public ModeOptions.ActionEnum getReplaceModeActionEnum() {
return switch (replaceMode) {
case ONLY_AIR -> ModeOptions.ActionEnum.REPLACE_ONLY_AIR;
case BLOCKS_AND_AIR -> ModeOptions.ActionEnum.REPLACE_BLOCKS_AND_AIR;
case ONLY_BLOCKS -> ModeOptions.ActionEnum.REPLACE_ONLY_BLOCKS;
case FILTERED_BY_OFFHAND -> ModeOptions.ActionEnum.REPLACE_FILTERED_BY_OFFHAND;
};
}
public void toggleProtectTileEntities() {
protectTileEntities = !protectTileEntities;
} }
public boolean shouldReplaceAir() { public boolean shouldReplaceAir() {
return replaceMode == ReplaceMode.ONLY_AIR || replaceMode == ReplaceMode.SOLID_AND_AIR; return replaceMode == ReplaceMode.ONLY_AIR || replaceMode == ReplaceMode.BLOCKS_AND_AIR;
} }
public boolean shouldReplaceSolid() { public boolean shouldReplaceBlocks() {
return replaceMode == ReplaceMode.SOLID_ONLY || replaceMode == ReplaceMode.SOLID_AND_AIR; return replaceMode == ReplaceMode.ONLY_BLOCKS || replaceMode == ReplaceMode.BLOCKS_AND_AIR;
} }
public boolean shouldReplaceFiltered() { public boolean shouldReplaceFiltered() {
return replaceMode == ReplaceMode.FILTERED_BY_OFFHAND; return replaceMode == ReplaceMode.FILTERED_BY_OFFHAND;
} }
public boolean shouldReplaceTileEntities() { public boolean shouldProtectTileEntities() {
return replaceTileEntities; return protectTileEntities;
} }
public boolean shouldOffsetStartPosition() { public boolean shouldOffsetStartPosition() {
return shouldReplaceSolid() || shouldReplaceFiltered(); return replaceMode != ReplaceMode.ONLY_AIR;
} }
} }

View File

@@ -19,6 +19,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
@@ -27,9 +28,7 @@ import nl.requios.effortlessbuilding.network.ServerBreakBlocksPacket;
import nl.requios.effortlessbuilding.network.ServerPlaceBlocksPacket; import nl.requios.effortlessbuilding.network.ServerPlaceBlocksPacket;
import nl.requios.effortlessbuilding.utilities.*; import nl.requios.effortlessbuilding.utilities.*;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
// Receives block placed events, then finds additional blocks we want to place through various systems, // Receives block placed events, then finds additional blocks we want to place through various systems,
// and then sends them to the server to be placed // and then sends them to the server to be placed
@@ -38,42 +37,46 @@ import java.util.List;
public class BuilderChain { public class BuilderChain {
private final BlockSet blocks = new BlockSet(); private final BlockSet blocks = new BlockSet();
private boolean blockInHand;
private boolean lookingAtInteractiveObject;
private Item previousHeldItem; private Item previousHeldItem;
private int soundTime = 0; private int soundTime = 0;
private BlockEntry startPosForPlacing;
private BlockPos startPosForBreaking;
private BlockHitResult lookingAtNear;
public enum State { public enum BuildingState {
IDLE, IDLE,
PLACING, PLACING,
BREAKING BREAKING
} }
private State state = State.IDLE; //What we are currently doing
private BuildingState buildingState = BuildingState.IDLE;
public enum AbilitiesState {
CAN_PLACE_AND_BREAK,
CAN_BREAK,
NONE
}
//Whether we can place or break blocks, determined by what we are looking at and what we are holding
private AbilitiesState abilitiesState = AbilitiesState.CAN_PLACE_AND_BREAK;
public void onRightClick() { public void onRightClick() {
if (lookingAtInteractiveObject) return; if (abilitiesState != AbilitiesState.CAN_PLACE_AND_BREAK || buildingState == BuildingState.BREAKING) {
var player = Minecraft.getInstance().player;
if (state == State.BREAKING) {
cancel(); cancel();
return; return;
} }
if (!blockInHand) { if (buildingState == BuildingState.IDLE) {
if (state == State.PLACING) cancel(); buildingState = BuildingState.PLACING;
return;
}
if (state == State.IDLE) {
state = State.PLACING;
} }
var player = Minecraft.getInstance().player;
var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode(); var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
//Find out if we should place blocks now //Find out if we should place blocks now
if (buildMode.instance.onClick(blocks)) { if (buildMode.instance.onClick(blocks)) {
state = State.IDLE; buildingState = BuildingState.IDLE;
if (!blocks.isEmpty()) { if (!blocks.isEmpty()) {
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksPlaced(blocks); EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksPlaced(blocks);
@@ -85,28 +88,29 @@ public class BuilderChain {
} }
public void onLeftClick() { public void onLeftClick() {
if (lookingAtInteractiveObject) return; if (abilitiesState == AbilitiesState.NONE || buildingState == BuildingState.PLACING) {
var player = Minecraft.getInstance().player;
if (state == State.PLACING) {
cancel(); cancel();
return; return;
} }
var player = Minecraft.getInstance().player;
if (!ReachHelper.canBreakFar(player)) return; if (!ReachHelper.canBreakFar(player)) return;
if (state == State.IDLE){ if (buildingState == BuildingState.IDLE){
state = State.BREAKING; buildingState = BuildingState.BREAKING;
//Recalculate block positions, because start position has changed //Use new start position for breaking, because we assumed the player was gonna place
onTick(); blocks.setStartPos(new BlockEntry(startPosForBreaking));
BuilderFilter.filterOnCoordinates(blocks, player);
findExistingBlockStates(player.level);
BuilderFilter.filterOnExistingBlockStates(blocks, player);
} }
var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode(); var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
//Find out if we should break blocks now //Find out if we should break blocks now
if (buildMode.instance.onClick(blocks)) { if (buildMode.instance.onClick(blocks)) {
state = State.IDLE; buildingState = BuildingState.IDLE;
if (!blocks.isEmpty()) { if (!blocks.isEmpty()) {
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksBroken(blocks); EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksBroken(blocks);
@@ -120,40 +124,45 @@ public class BuilderChain {
public void onTick() { public void onTick() {
var previousCoordinates = new HashSet<>(blocks.getCoordinates()); var previousCoordinates = new HashSet<>(blocks.getCoordinates());
blocks.clear(); blocks.clear();
startPosForPlacing = null;
startPosForBreaking = null;
lookingAtNear = null;
var mc = Minecraft.getInstance(); var mc = Minecraft.getInstance();
var player = mc.player; var player = mc.player;
var world = mc.level; var world = mc.level;
//Check if we have a BlockItem in hand abilitiesState = determineAbilities(mc, player, world);
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); if (abilitiesState == AbilitiesState.NONE) return;
blockInHand = CompatHelper.isItemBlockProxy(itemStack);
lookingAtInteractiveObject = BlockUtilities.determineIfLookingAtInteractiveObject(mc, world);
if (lookingAtInteractiveObject) return;
var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode(); var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
var modifierSettings = ModifierSettingsManager.getModifierSettings(player); var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
if (state == State.IDLE) { if (buildingState == BuildingState.IDLE) {
//Find start position //Find start position
BlockHitResult lookingAt = ClientEvents.getLookingAtFar(player); BlockEntry startEntry = findStartPosition(player, buildMode);
BlockEntry startEntry = findStartPosition(player, lookingAt);
if (startEntry != null) { if (startEntry != null) {
blocks.add(startEntry); blocks.setStartPos(startEntry);
blocks.firstPos = startEntry.blockPos; } else {
blocks.lastPos = startEntry.blockPos; //We aren't placing or breaking blocks, and we have no start position
abilitiesState = AbilitiesState.NONE;
return;
} }
} }
EffortlessBuildingClient.BUILD_MODES.findCoordinates(blocks, player, buildMode); EffortlessBuildingClient.BUILD_MODES.findCoordinates(blocks, player, buildMode);
EffortlessBuildingClient.BUILD_MODIFIERS.findCoordinates(blocks, player, modifierSettings); EffortlessBuildingClient.BUILD_MODIFIERS.findCoordinates(blocks, player, modifierSettings);
BuilderFilter.filterOnCoordinates(blocks, player); BuilderFilter.filterOnCoordinates(blocks, player);
if (buildMode == BuildModeEnum.DISABLED && blocks.size() <= 1) {
abilitiesState = AbilitiesState.NONE;
return;
}
findExistingBlockStates(world); findExistingBlockStates(world);
BuilderFilter.filterOnExistingBlockStates(blocks, player); BuilderFilter.filterOnExistingBlockStates(blocks, player);
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
findNewBlockStates(player, itemStack); findNewBlockStates(player, itemStack);
BuilderFilter.filterOnNewBlockStates(blocks, player); BuilderFilter.filterOnNewBlockStates(blocks, player);
@@ -165,6 +174,28 @@ public class BuilderChain {
previousHeldItem = itemStack.getItem(); previousHeldItem = itemStack.getItem();
} }
//Whether we can place or break blocks, determined by what we are looking at and what we are holding
private AbilitiesState determineAbilities(Minecraft mc, Player player, Level world) {
var hitResult = Minecraft.getInstance().hitResult;
if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) {
lookingAtNear = (BlockHitResult) hitResult;
}
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack);
boolean lookingAtInteractiveObject = BlockUtilities.determineIfLookingAtInteractiveObject(mc, world);
boolean isShiftKeyDown = player.isShiftKeyDown();
if (lookingAtInteractiveObject && !isShiftKeyDown)
return AbilitiesState.NONE;
if (!blockInHand)
return AbilitiesState.CAN_BREAK;
return AbilitiesState.CAN_PLACE_AND_BREAK;
}
private void onBlocksChanged(Player player) { private void onBlocksChanged(Player player) {
//Renew randomness of randomizer bag //Renew randomness of randomizer bag
@@ -177,64 +208,77 @@ public class BuilderChain {
if (blocks.getLastBlockEntry() != null && blocks.getLastBlockEntry().newBlockState != null) { if (blocks.getLastBlockEntry() != null && blocks.getLastBlockEntry().newBlockState != null) {
var lastBlockState = blocks.getLastBlockEntry().newBlockState; var lastBlockState = blocks.getLastBlockEntry().newBlockState;
SoundType soundType = lastBlockState.getBlock().getSoundType(lastBlockState, player.level, blocks.lastPos, player); SoundType soundType = lastBlockState.getBlock().getSoundType(lastBlockState, player.level, blocks.lastPos, player);
SoundEvent soundEvent = state == BuilderChain.State.BREAKING ? soundType.getBreakSound() : soundType.getPlaceSound(); SoundEvent soundEvent = buildingState == BuildingState.BREAKING ? soundType.getBreakSound() : soundType.getPlaceSound();
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.3f, 0.8f); player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.3f, 0.8f);
} }
} }
} }
public void cancel() { public void cancel() {
if (state == State.IDLE) return; if (buildingState == BuildingState.IDLE) return;
state = State.IDLE; buildingState = BuildingState.IDLE;
EffortlessBuildingClient.BUILD_MODES.onCancel(); EffortlessBuildingClient.BUILD_MODES.onCancel();
Minecraft.getInstance().player.playSound(SoundEvents.UI_TOAST_OUT, 4, 1); Minecraft.getInstance().player.playSound(SoundEvents.UI_TOAST_OUT, 4, 1);
} }
private BlockEntry findStartPosition(Player player, BlockHitResult lookingAtFar) { private BlockEntry findStartPosition(Player player, BuildModeEnum buildMode) {
if (lookingAtFar == null || lookingAtFar.getType() == HitResult.Type.MISS) return null;
var startPos = lookingAtFar.getBlockPos(); //Determine if we should look far or nearby
boolean shouldLookAtNear = buildMode == BuildModeEnum.DISABLED;
BlockHitResult lookingAt;
if (shouldLookAtNear) {
lookingAt = lookingAtNear;
} else {
lookingAt = ClientEvents.getLookingAtFar(player);
}
if (lookingAt == null || lookingAt.getType() == HitResult.Type.MISS) return null;
var startPos = lookingAt.getBlockPos();
//Check if out of reach //Check if out of reach
int maxReach = ReachHelper.getMaxReach(player); int maxReach = ReachHelper.getMaxReach(player);
if (player.blockPosition().distSqr(startPos) > maxReach * maxReach) return null; if (player.blockPosition().distSqr(startPos) > maxReach * maxReach) return null;
//TODO we are always at IDLE state here, find another way to check if we are breaking startPosForBreaking = startPos;
if (state != State.BREAKING) { if (!shouldLookAtNear && !ReachHelper.canBreakFar(player)) {
startPosForBreaking = null;
}
if (abilitiesState == AbilitiesState.CAN_PLACE_AND_BREAK) {
//Calculate start position for placing
//Offset in direction of sidehit if not quickreplace and not replaceable //Offset in direction of sidehit if not quickreplace and not replaceable
boolean shouldOffsetStartPosition = EffortlessBuildingClient.BUILD_SETTINGS.shouldOffsetStartPosition(); boolean shouldOffsetStartPosition = EffortlessBuildingClient.BUILD_SETTINGS.shouldOffsetStartPosition();
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos); boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos);
if (!shouldOffsetStartPosition && !replaceable && !becomesDoubleSlab) { if (!shouldOffsetStartPosition && !replaceable && !becomesDoubleSlab) {
startPos = startPos.relative(lookingAtFar.getDirection()); startPos = startPos.relative(lookingAt.getDirection());
} }
//Get under tall grass and other replaceable blocks //Get under tall grass and other replaceable blocks
if (shouldOffsetStartPosition && replaceable) { if (shouldOffsetStartPosition && replaceable) {
startPos = startPos.below(); startPos = startPos.below();
} }
} else { } else {
//We can only break
//Do not break far if we are not allowed to //Do not break far if we are not allowed to
if (!ReachHelper.canBreakFar(player)) { if (startPosForBreaking == null) return null;
boolean startPosIsNear = false;
var lookingAtNear = Minecraft.getInstance().hitResult;
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK) {
startPosIsNear = ((BlockHitResult) lookingAtNear).getBlockPos().equals(startPos);
}
if (!startPosIsNear) return null;
}
} }
var blockEntry = new BlockEntry(startPos); var blockEntry = new BlockEntry(startPos);
//Place upside-down stairs if we aim high at block //Place upside-down stairs if we aim high at block
var hitVec = lookingAtFar.getLocation(); var hitVec = lookingAt.getLocation();
//Format hitvec to 0.x //Format hitvec to 0.x
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z))); hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z)));
if (hitVec.y > 0.5) { if (hitVec.y > 0.5) {
blockEntry.mirrorY = true; blockEntry.mirrorY = true;
} }
startPosForPlacing = blockEntry;
return blockEntry; return blockEntry;
} }
@@ -245,7 +289,7 @@ public class BuilderChain {
} }
private void findNewBlockStates(Player player, ItemStack itemStack) { private void findNewBlockStates(Player player, ItemStack itemStack) {
if (state == State.BREAKING) return; if (buildingState == BuildingState.BREAKING) return;
if (itemStack.getItem() instanceof BlockItem) { if (itemStack.getItem() instanceof BlockItem) {
@@ -264,20 +308,39 @@ public class BuilderChain {
} }
} }
public State getState() {
return state;
}
public BlockSet getBlocks() { public BlockSet getBlocks() {
return blocks; return blocks;
} }
public boolean isBlockInHand() { public BuildingState getBuildingState() {
return blockInHand; return buildingState;
} }
public boolean isLookingAtInteractiveObject() { public AbilitiesState getAbilitiesState() {
return lookingAtInteractiveObject; return abilitiesState;
}
public BuildingState getPretendBuildingState() {
if (buildingState != BuildingState.IDLE) return buildingState;
if (abilitiesState == AbilitiesState.CAN_PLACE_AND_BREAK) return BuildingState.PLACING;
if (abilitiesState == AbilitiesState.CAN_BREAK) return BuildingState.BREAKING;
return BuildingState.IDLE;
}
public BlockEntry getStartPosForPlacing() {
return startPosForPlacing;
}
public BlockPos getStartPosForBreaking() {
return startPosForBreaking;
}
public BlockEntry getStartPos() {
if (getPretendBuildingState() == BuildingState.BREAKING) return new BlockEntry(getStartPosForBreaking());
return getStartPosForPlacing();
}
public BlockHitResult getLookingAtNear() {
return lookingAtNear;
} }
} }

View File

@@ -5,7 +5,6 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet; import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.PlaceChecker; import nl.requios.effortlessbuilding.utilities.PlaceChecker;
@@ -17,7 +16,8 @@ public class BuilderFilter {
public static void filterOnExistingBlockStates(BlockSet blocks, Player player) { public static void filterOnExistingBlockStates(BlockSet blocks, Player player) {
var buildSettings = EffortlessBuildingClient.BUILD_SETTINGS; var buildSettings = EffortlessBuildingClient.BUILD_SETTINGS;
boolean placing = EffortlessBuildingClient.BUILDER_CHAIN.getState() == BuilderChain.State.PLACING; var buildingState = EffortlessBuildingClient.BUILDER_CHAIN.getPretendBuildingState();
boolean placing = buildingState == BuilderChain.BuildingState.PLACING;
var iter = blocks.entrySet().iterator(); var iter = blocks.entrySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
@@ -25,12 +25,13 @@ public class BuilderFilter {
var blockState = blockEntry.existingBlockState; var blockState = blockEntry.existingBlockState;
boolean remove = false; boolean remove = false;
if (!buildSettings.shouldReplaceTileEntities() && blockState.hasBlockEntity()) remove = true; if (buildSettings.shouldProtectTileEntities() && blockState.hasBlockEntity()) remove = true;
if (placing) { if (placing && !buildSettings.shouldReplaceFiltered()) {
if (!buildSettings.shouldReplaceAir() && blockState.isAir()) remove = true; if (!buildSettings.shouldReplaceAir() && blockState.isAir()) remove = true;
boolean isSolid = blockState.isRedstoneConductor(player.level, blockEntry.blockPos); boolean isReplaceable = blockState.getMaterial().isReplaceable();
if (!buildSettings.shouldReplaceSolid() && isSolid) remove = true; // boolean isSolid = blockState.isRedstoneConductor(player.level, blockEntry.blockPos);
if (!buildSettings.shouldReplaceBlocks() && !isReplaceable) remove = true;
} }
if (buildSettings.shouldReplaceFiltered()) { if (buildSettings.shouldReplaceFiltered()) {
@@ -43,13 +44,16 @@ public class BuilderFilter {
} }
public static void filterOnNewBlockStates(BlockSet blocks, Player player) { public static void filterOnNewBlockStates(BlockSet blocks, Player player) {
var buildSettings = EffortlessBuildingClient.BUILD_SETTINGS;
var buildingState = EffortlessBuildingClient.BUILDER_CHAIN.getPretendBuildingState();
boolean placing = buildingState == BuilderChain.BuildingState.PLACING;
var iter = blocks.entrySet().iterator(); var iter = blocks.entrySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
var blockEntry = iter.next().getValue(); var blockEntry = iter.next().getValue();
boolean remove = false; boolean remove = false;
if (!PlaceChecker.shouldPlaceBlock(player.level, blockEntry)) remove = true; if (placing && !PlaceChecker.shouldPlaceBlock(player.level, blockEntry)) remove = true;
if (remove) iter.remove(); if (remove) iter.remove();
} }

View File

@@ -6,8 +6,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet; import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.utilities.InventoryHelper; import nl.requios.effortlessbuilding.utilities.InventoryHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper; import nl.requios.effortlessbuilding.utilities.SurvivalHelper;

View File

@@ -30,7 +30,7 @@ public class ServerBlockPlacer {
private boolean isPlacingOrBreakingBlocks = false; private boolean isPlacingOrBreakingBlocks = false;
public void placeBlocks(Player player, BlockSet blocks) { public void placeBlocks(Player player, BlockSet blocks) {
EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks"); // EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
for (BlockEntry block : blocks) { for (BlockEntry block : blocks) {
placeBlock(player, block); placeBlock(player, block);
@@ -47,7 +47,7 @@ public class ServerBlockPlacer {
} }
public void breakBlocks(Player player, BlockSet blocks) { public void breakBlocks(Player player, BlockSet blocks) {
EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks"); // EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
for (BlockEntry block : blocks) { for (BlockEntry block : blocks) {
breakBlock(player, block); breakBlock(player, block);

View File

@@ -7,6 +7,11 @@ public class ServerBuildState {
private static final String IS_USING_BUILD_MODE_KEY = EffortlessBuilding.MODID + ":isUsingBuildMode"; private static final String IS_USING_BUILD_MODE_KEY = EffortlessBuilding.MODID + ":isUsingBuildMode";
private static final String IS_QUICK_REPLACING_KEY = EffortlessBuilding.MODID + ":isQuickReplacing"; private static final String IS_QUICK_REPLACING_KEY = EffortlessBuilding.MODID + ":isQuickReplacing";
public static void handleNewPlayer(Player player) {
setIsUsingBuildMode(player, false);
setIsQuickReplacing(player, false);
}
public static boolean isUsingBuildMode(Player player) { public static boolean isUsingBuildMode(Player player) {
return player.getPersistentData().contains(IS_USING_BUILD_MODE_KEY); return player.getPersistentData().contains(IS_USING_BUILD_MODE_KEY);
} }

View File

@@ -1,4 +1,4 @@
package nl.requios.effortlessbuilding.buildmodifier; package nl.requios.effortlessbuilding.systems;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@@ -10,12 +10,14 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.utilities.FixedStack; import nl.requios.effortlessbuilding.utilities.FixedStack;
import nl.requios.effortlessbuilding.utilities.InventoryHelper; import nl.requios.effortlessbuilding.utilities.InventoryHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper; import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import java.util.*; import java.util.*;
//Server only
public class UndoRedo { public class UndoRedo {
//Undo and redo stacks per player //Undo and redo stacks per player

View File

@@ -1,9 +1,10 @@
package nl.requios.effortlessbuilding.utilities; package nl.requios.effortlessbuilding.utilities;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -12,6 +13,7 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
//Common
public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<BlockEntry> { public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<BlockEntry> {
public static boolean logging = true; public static boolean logging = true;
@@ -35,20 +37,19 @@ public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<
} }
} }
public void setStartPos(BlockEntry startPos) {
clear();
add(startPos);
firstPos = startPos.blockPos;
lastPos = startPos.blockPos;
}
public void add(BlockEntry blockEntry) { public void add(BlockEntry blockEntry) {
if (!containsKey(blockEntry.blockPos)) { if (!containsKey(blockEntry.blockPos)) {
if (!DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> () -> ClientSide.isFull(this))) {
//Limit number of blocks you can place put(blockEntry.blockPos, blockEntry);
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(Minecraft.getInstance().player);
if (size() >= limit) {
if (logging) EffortlessBuilding.log("BlockSet limit reached, not adding block at " + blockEntry.blockPos);
return;
} }
put(blockEntry.blockPos, blockEntry);
} else { } else {
if (logging) EffortlessBuilding.log("BlockSet already contains block at " + blockEntry.blockPos); if (logging) EffortlessBuilding.log("BlockSet already contains block at " + blockEntry.blockPos);
} }
} }
@@ -78,4 +79,17 @@ public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<
public static BlockSet decode(FriendlyByteBuf buf) { public static BlockSet decode(FriendlyByteBuf buf) {
return new BlockSet(buf.readList(BlockEntry::decode)); return new BlockSet(buf.readList(BlockEntry::decode));
} }
@OnlyIn(Dist.CLIENT)
public static class ClientSide {
public static boolean isFull(BlockSet blockSet) {
//Limit number of blocks you can place
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(net.minecraft.client.Minecraft.getInstance().player);
if (blockSet.size() >= limit) {
if (logging) EffortlessBuilding.log("BlockSet limit reached, not adding block.");
return true;
}
return false;
}
}
} }

View File

@@ -1,7 +1,6 @@
package nl.requios.effortlessbuilding.utilities; package nl.requios.effortlessbuilding.utilities;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
@@ -9,6 +8,7 @@ import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@@ -16,6 +16,7 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
//Common
public class BlockUtilities { public class BlockUtilities {
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) { public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) {
@@ -25,7 +26,7 @@ public class BlockUtilities {
return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult)); return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult));
} }
public static boolean determineIfLookingAtInteractiveObject(Minecraft mc, ClientLevel level) { public static boolean determineIfLookingAtInteractiveObject(Minecraft mc, Level level) {
//Check if we are looking at an interactive object //Check if we are looking at an interactive object
var result = false; var result = false;
if (mc.hitResult != null) { if (mc.hitResult != null) {

View File

@@ -17,13 +17,16 @@ public class PlaceChecker {
//SchematicPrinter::shouldPlaceBlock //SchematicPrinter::shouldPlaceBlock
public static boolean shouldPlaceBlock(Level world, BlockEntry blockEntry) { public static boolean shouldPlaceBlock(Level world, BlockEntry blockEntry) {
if (world == null) if (world == null || blockEntry == null)
return false; return false;
var pos = blockEntry.blockPos; var pos = blockEntry.blockPos;
var state = blockEntry.newBlockState; var state = blockEntry.newBlockState;
BlockEntity tileEntity = null; BlockEntity tileEntity = null;
if (state == null)
return false;
BlockState toReplace = world.getBlockState(pos); BlockState toReplace = world.getBlockState(pos);
BlockEntity toReplaceTE = world.getBlockEntity(pos); BlockEntity toReplaceTE = world.getBlockEntity(pos);
BlockState toReplaceOther = null; BlockState toReplaceOther = null;

View File

@@ -6,6 +6,7 @@ import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
//Common
public class ReachHelper { public class ReachHelper {
private static final String REACH_UPGRADE_KEY = EffortlessBuilding.MODID + ":reachUpgrade"; private static final String REACH_UPGRADE_KEY = EffortlessBuilding.MODID + ":reachUpgrade";

View File

@@ -1,11 +1,12 @@
package nl.requios.effortlessbuilding.buildmodifier; package nl.requios.effortlessbuilding.utilities;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import java.util.List; import java.util.List;
//Used only for Undo //Server only
@Deprecated
public class UndoRedoBlockSet { public class UndoRedoBlockSet {
private final List<BlockPos> coordinates; private final List<BlockPos> coordinates;
private final List<BlockState> previousBlockStates; private final List<BlockState> previousBlockStates;

View File

@@ -1,79 +1,87 @@
{ {
"effortlessbuilding.screen.modifier_settings": "Modifier Settings", "effortlessbuilding.screen.modifier_settings": "Modifier Settings",
"effortlessbuilding.screen.radial_menu": "Build Modes", "effortlessbuilding.screen.radial_menu": "Build Modes",
"effortlessbuilding.screen.player_settings": "Player Settings", "effortlessbuilding.screen.player_settings": "Player Settings",
"key.effortlessbuilding.category": "Effortless Building", "key.effortlessbuilding.category": "Effortless Building",
"key.effortlessbuilding.hud.desc": "Modifier Menu", "key.effortlessbuilding.hud.desc": "Modifier Menu",
"key.effortlessbuilding.replace.desc": "Toggle QuickReplace", "key.effortlessbuilding.replace.desc": "Toggle QuickReplace",
"key.effortlessbuilding.mode.desc": "Radial Menu", "key.effortlessbuilding.mode.desc": "Radial Menu",
"key.effortlessbuilding.undo.desc": "Undo", "key.effortlessbuilding.undo.desc": "Undo",
"key.effortlessbuilding.redo.desc": "Redo", "key.effortlessbuilding.redo.desc": "Redo",
"key.effortlessbuilding.altplacement.desc": "Alternative placement", "key.effortlessbuilding.altplacement.desc": "Alternative placement",
"item.effortlessbuilding.randomizer_bag": "Leather Randomizer Bag", "item.effortlessbuilding.randomizer_bag": "Leather Randomizer Bag",
"item.effortlessbuilding.golden_randomizer_bag": "Golden Randomizer Bag", "item.effortlessbuilding.golden_randomizer_bag": "Golden Randomizer Bag",
"item.effortlessbuilding.diamond_randomizer_bag": "Diamond Randomizer Bag", "item.effortlessbuilding.diamond_randomizer_bag": "Diamond Randomizer Bag",
"item.effortlessbuilding.reach_upgrade1": "Reach Upgrade 1", "item.effortlessbuilding.reach_upgrade1": "Reach Upgrade 1",
"item.effortlessbuilding.reach_upgrade2": "Reach Upgrade 2", "item.effortlessbuilding.reach_upgrade2": "Reach Upgrade 2",
"item.effortlessbuilding.reach_upgrade3": "Reach Upgrade 3", "item.effortlessbuilding.reach_upgrade3": "Reach Upgrade 3",
"effortlessbuilding.mode.normal": "Disable", "effortlessbuilding.mode.normal": "Disable",
"effortlessbuilding.mode.normal_plus": "Single", "effortlessbuilding.mode.normal_plus": "Single",
"effortlessbuilding.mode.line": "Line", "effortlessbuilding.mode.line": "Line",
"effortlessbuilding.mode.wall": "Wall", "effortlessbuilding.mode.wall": "Wall",
"effortlessbuilding.mode.floor": "Floor", "effortlessbuilding.mode.floor": "Floor",
"effortlessbuilding.mode.diagonal_line": "Diagonal Line", "effortlessbuilding.mode.diagonal_line": "Diagonal Line",
"effortlessbuilding.mode.diagonal_wall": "Diagonal Wall", "effortlessbuilding.mode.diagonal_wall": "Diagonal Wall",
"effortlessbuilding.mode.slope_floor": "Slope Floor", "effortlessbuilding.mode.slope_floor": "Slope Floor",
"effortlessbuilding.mode.cube": "Cube", "effortlessbuilding.mode.cube": "Cube",
"effortlessbuilding.mode.circle": "Circle", "effortlessbuilding.mode.circle": "Circle",
"effortlessbuilding.mode.cylinder": "Cylinder", "effortlessbuilding.mode.cylinder": "Cylinder",
"effortlessbuilding.mode.sphere": "Sphere", "effortlessbuilding.mode.sphere": "Sphere",
"effortlessbuilding.mode.pyramid": "Pyramid", "effortlessbuilding.mode.pyramid": "Pyramid",
"effortlessbuilding.mode.cone": "Cone", "effortlessbuilding.mode.cone": "Cone",
"effortlessbuilding.mode.dome": "Dome", "effortlessbuilding.mode.dome": "Dome",
"effortlessbuilding.modedescription.normal": "Disable mod and use vanilla placement rules",
"effortlessbuilding.modedescription.normal_plus": "Like vanilla, but with increased reach and placement preview",
"effortlessbuilding.modedescription.line": "",
"effortlessbuilding.modedescription.wall": "",
"effortlessbuilding.modedescription.floor": "",
"effortlessbuilding.modedescription.diagonal_line": "",
"effortlessbuilding.modedescription.diagonal_wall": "",
"effortlessbuilding.modedescription.slope_floor": "",
"effortlessbuilding.modedescription.cube": "",
"effortlessbuilding.modedescription.circle": "",
"effortlessbuilding.modedescription.cylinder": "",
"effortlessbuilding.modedescription.sphere": "",
"effortlessbuilding.modedescription.pyramid": "",
"effortlessbuilding.modedescription.cone": "",
"effortlessbuilding.modedescription.dome": "",
"effortlessbuilding.action.undo": "Undo", "effortlessbuilding.modedescription.normal": "Disable mod and use vanilla placement rules",
"effortlessbuilding.action.redo": "Redo", "effortlessbuilding.modedescription.normal_plus": "Like vanilla, but with increased reach and placement preview",
"effortlessbuilding.action.replace": "Replace", "effortlessbuilding.modedescription.line": "",
"effortlessbuilding.action.open_modifier_settings": "Open Modifier Settings", "effortlessbuilding.modedescription.wall": "",
"effortlessbuilding.action.open_player_settings": "Open Settings", "effortlessbuilding.modedescription.floor": "",
"effortlessbuilding.modedescription.diagonal_line": "",
"effortlessbuilding.action.build_speed": "Build Speed", "effortlessbuilding.modedescription.diagonal_wall": "",
"effortlessbuilding.action.filling": "Filling", "effortlessbuilding.modedescription.slope_floor": "",
"effortlessbuilding.action.raised_edge": "Raised Edge", "effortlessbuilding.modedescription.cube": "",
"effortlessbuilding.action.thickness": "Line Thickness", "effortlessbuilding.modedescription.circle": "",
"effortlessbuilding.action.circle_start": "Start Point", "effortlessbuilding.modedescription.cylinder": "",
"effortlessbuilding.modedescription.sphere": "",
"effortlessbuilding.modedescription.pyramid": "",
"effortlessbuilding.modedescription.cone": "",
"effortlessbuilding.modedescription.dome": "",
"effortlessbuilding.action.normal_speed": "Normal", "effortlessbuilding.action.undo": "Undo",
"effortlessbuilding.action.fast_speed": "Fast", "effortlessbuilding.action.redo": "Redo",
"effortlessbuilding.action.full": "Filled", "effortlessbuilding.action.open_modifier_settings": "Open Modifier Settings",
"effortlessbuilding.action.hollow": "Hollow", "effortlessbuilding.action.open_player_settings": "Open Build Settings",
"effortlessbuilding.action.skeleton": "Skeleton",
"effortlessbuilding.action.short_edge": "Short Edge",
"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 <level>" "effortlessbuilding.action.replace_only_air": "Don't Replace Blocks",
"effortlessbuilding.action.replace_only_air.description": "You will only place blocks where there is no block already. Replaceables such as grass will still be replaced.",
"effortlessbuilding.action.replace_blocks_and_air": "Replace Blocks And Air",
"effortlessbuilding.action.replace_blocks_and_air.description": "You will replace blocks and air.",
"effortlessbuilding.action.replace_only_blocks": "Replace Only Blocks",
"effortlessbuilding.action.replace_only_blocks.description": "You will only replace blocks, not air.",
"effortlessbuilding.action.replace_filtered_by_offhand": "Filter By Offhand",
"effortlessbuilding.action.replace_filtered_by_offhand.description": "You will only replace blocks that match the block in your offhand. If you don't have a block in your offhand, you will only replace air. If you have a randomizer bag in your offhand, you will only replace blocks that are contained in the bag.",
"effortlessbuilding.action.toggle_protect_tile_entities": "Protect Tile Entities",
"effortlessbuilding.action.toggle_protect_tile_entities.description": "Blocks that hold data such as chests, furnaces, and hoppers will not be replaced.",
"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",
"effortlessbuilding.action.full": "Filled",
"effortlessbuilding.action.hollow": "Hollow",
"effortlessbuilding.action.skeleton": "Skeleton",
"effortlessbuilding.action.short_edge": "Short Edge",
"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"
} }