From 9070dc0575dd7e2c7b1226b7e22d27245c00ebb8 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Sun, 21 Apr 2024 15:51:24 +0200 Subject: [PATCH] Improvements for free blocks management (#9911) Adds a button in the TH permissions page to obtain a Permission Scepter (which already existed, but was creative-only and nobody knew about it): image Changes the free interaction list to display the friendly name of the block (and fix up some size/wrapping issues): You still have to type the block id if you want to add a block via the GUI. image Entering a non-existent block id is now ignored, instead of adding Air. Makes UI buttons disable properly when you don't have Edit Permissions permission. The Permission Scepter tooltip shows whether it's in tool or location mode. image Holding CTRL when clicking a block with the Permission Scepter now removes that block/location from the list. Why not SHIFT? Because you'll often also have to hold shift in order to avoid triggering interactive blocks -- and interactive blocks are the whole point. While holding a Permission Scepter, green boxes will be shown around free interaction blocks/locations. It shows either blocks or locations depending on the current tool mode. Locations will be shown colony-wide. Blocks will only be shown if you're relatively close (for performance reasons). --- .../api/util/constant/WindowConstants.java | 5 + .../translation/ToolTranslationConstants.java | 2 + .../gui/townhall/WindowPermissionsPage.java | 37 ++++++- .../core/items/ItemScepterPermission.java | 102 ++++++++++++++++-- .../ChangeFreeToInteractBlockMessage.java | 9 +- .../gui/townhall/layoutpermissions.xml | 28 ++--- .../minecolonies/lang/manual_en_us.json | 1 + .../textures/gui/scepterpermission.png | Bin 0 -> 14189 bytes 8 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 src/main/resources/assets/minecolonies/textures/gui/scepterpermission.png diff --git a/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java b/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java index 462499b27c7..7e1b586f27c 100755 --- a/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/WindowConstants.java @@ -397,6 +397,11 @@ public final class WindowConstants */ public static final String BUTTON_REMOVE_BLOCK = "removeBlock"; + /** + * Button to obtain a permissions scepter. + */ + public static final String BUTTON_BLOCK_TOOL = "blockTool"; + /** * Button to select a block for a replacement. */ diff --git a/src/main/java/com/minecolonies/api/util/constant/translation/ToolTranslationConstants.java b/src/main/java/com/minecolonies/api/util/constant/translation/ToolTranslationConstants.java index 6907184333c..74aee42ec53 100644 --- a/src/main/java/com/minecolonies/api/util/constant/translation/ToolTranslationConstants.java +++ b/src/main/java/com/minecolonies/api/util/constant/translation/ToolTranslationConstants.java @@ -93,6 +93,8 @@ public class ToolTranslationConstants @NonNls public static final String TOOL_PERMISSION_SCEPTER_SET_MODE = "com.minecolonies.coremod.item.permissionscepter.setmode"; @NonNls + public static final String TOOL_PERMISSION_SCEPTER_MODE = "com.minecolonies.coremod.item.permissionscepter.mode"; + @NonNls public static final String TOOL_PERMISSION_SCEPTER_MODE_BLOCK = "com.minecolonies.coremod.item.permissionscepter.mode.block"; @NonNls public static final String TOOL_PERMISSION_SCEPTER_MODE_LOCATION = "com.minecolonies.coremod.item.permissionscepter.mode.location"; diff --git a/src/main/java/com/minecolonies/core/client/gui/townhall/WindowPermissionsPage.java b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowPermissionsPage.java index 444ceedcca0..2f089bfb1ab 100644 --- a/src/main/java/com/minecolonies/core/client/gui/townhall/WindowPermissionsPage.java +++ b/src/main/java/com/minecolonies/core/client/gui/townhall/WindowPermissionsPage.java @@ -9,11 +9,14 @@ import com.ldtteam.blockui.views.DropDownList; import com.ldtteam.blockui.views.ScrollingList; import com.minecolonies.api.colony.permissions.*; +import com.minecolonies.api.items.ModItems; import com.minecolonies.api.util.BlockPosUtil; import com.minecolonies.api.util.SoundUtils; import com.minecolonies.core.colony.buildings.workerbuildings.BuildingTownHall; import com.minecolonies.core.network.messages.PermissionsMessage; import com.minecolonies.core.network.messages.server.colony.ChangeFreeToInteractBlockMessage; +import com.minecolonies.core.network.messages.server.colony.building.GiveToolMessage; +import net.minecraft.ChatFormatting; import net.minecraft.ResourceLocationException; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; @@ -117,6 +120,7 @@ public WindowPermissionsPage(final BuildingTownHall.View building) registerButton(BUTTON_TRIGGER, this::trigger); registerButton(BUTTON_ADD_BLOCK, this::addBlock); registerButton(BUTTON_REMOVE_BLOCK, this::removeBlock); + registerButton(BUTTON_BLOCK_TOOL, this::giveBlockTool); registerButton(BUTTON_ADD_RANK, this::addRank); registerButton(TOWNHALL_RANK_BUTTON, this::onRankButtonClicked); registerButton(BUTTON_REMOVE_RANK, this::onRemoveRankButtonClicked); @@ -256,7 +260,8 @@ public void onOpened() final TextField playerNameField = findPaneOfTypeByID(INPUT_ADDPLAYER_NAME, TextField.class); final TextField rankNameField = findPaneOfTypeByID(INPUT_ADDRANK_NAME, TextField.class); final Button addRankButton = findPaneOfTypeByID(BUTTON_ADD_RANK, Button.class); - + final Button addBlockButton = findPaneOfTypeByID(BUTTON_ADD_BLOCK, Button.class); + final Button blockToolButton = findPaneOfTypeByID(BUTTON_BLOCK_TOOL, Button.class); if (building.getColony().getPermissions().hasPermission(player, Action.EDIT_PERMISSIONS)) { @@ -264,6 +269,8 @@ public void onOpened() playerNameField.setEnabled(true); rankNameField.setEnabled(true); addRankButton.setEnabled(true); + addBlockButton.setEnabled(true); + blockToolButton.setEnabled(true); } else { @@ -279,6 +286,8 @@ public void onOpened() addPlayerButton.setEnabled(false); playerNameField.setEnabled(false); addRankButton.setEnabled(false); + addBlockButton.setEnabled(false); + blockToolButton.setEnabled(false); } findPaneOfTypeByID(TOWNHALL_RANK_TYPE_PICKER, DropDownList.class).setSelectedIndex(actionsRank.isColonyManager() ? 0 : (actionsRank.isHostile() ? 1 : 2)); @@ -422,6 +431,14 @@ else if (row < freeBlocks.size() + freePositions.size()) } } + /** + * Gives the player a Permission Scepter. + */ + private void giveBlockTool(final Button button) + { + Network.getNetwork().sendToServer(new GiveToolMessage(buildingView, ModItems.permTool)); + } + /** * Fills the free blocks list in the GUI. */ @@ -444,13 +461,25 @@ public void updateElement(final int index, @NotNull final Pane rowPane) { if (index < freeBlocks.size()) { - rowPane.findPaneOfTypeByID(NAME_LABEL, Text.class).setText(Component.literal(BuiltInRegistries.BLOCK.getKey(freeBlocks.get(index)).toString())); + final Block block = freeBlocks.get(index); + final MutableComponent text = Component.literal(ForgeRegistries.BLOCKS.getKey(block).toString()); + text.append(Component.literal("\n")).append(block.getName().withStyle(ChatFormatting.DARK_GRAY)); + rowPane.findPaneOfTypeByID(NAME_LABEL, Text.class).setText(text); } else { final BlockPos pos = freePositions.get(index - freeBlocks.size()); - rowPane.findPaneOfTypeByID(NAME_LABEL, Text.class).setText(Component.literal(pos.getX() + " " + pos.getY() + " " + pos.getZ())); + final MutableComponent text = Component.literal(pos.getX() + " " + pos.getY() + " " + pos.getZ()); + if (building.getColony().getWorld().isLoaded(pos)) + { + final BlockState state = building.getColony().getWorld().getBlockState(pos); + text.append(Component.literal("\n")).append(state.getBlock().getName().withStyle(ChatFormatting.DARK_GRAY)); + } + rowPane.findPaneOfTypeByID(NAME_LABEL, Text.class).setText(text); } + + final boolean canEdit = building.getColony().getPermissions().hasPermission(Minecraft.getInstance().player, Action.EDIT_PERMISSIONS); + rowPane.findPaneOfTypeByID(BUTTON_REMOVE_BLOCK, Button.class).setEnabled(canEdit); } }); } @@ -467,7 +496,7 @@ private void addBlock() { final Block block = BuiltInRegistries.BLOCK.get(new ResourceLocation(inputText)); - if (block != null) + if (block != null && !block.defaultBlockState().isAir()) { building.getColony().addFreeBlock(block); new ChangeFreeToInteractBlockMessage(building.getColony(), block, ChangeFreeToInteractBlockMessage.MessageType.ADD_BLOCK).sendToServer(); diff --git a/src/main/java/com/minecolonies/core/items/ItemScepterPermission.java b/src/main/java/com/minecolonies/core/items/ItemScepterPermission.java index 72b3754573b..c1d7156902b 100755 --- a/src/main/java/com/minecolonies/core/items/ItemScepterPermission.java +++ b/src/main/java/com/minecolonies/core/items/ItemScepterPermission.java @@ -2,28 +2,43 @@ import com.minecolonies.api.colony.IColonyManager; import com.minecolonies.api.colony.IColonyView; +import com.minecolonies.api.colony.permissions.Action; +import com.minecolonies.api.items.IBlockOverlayItem; import com.minecolonies.api.util.MessageUtils; import com.minecolonies.core.network.messages.server.colony.ChangeFreeToInteractBlockMessage; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.InteractionResultHolder; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.InteractionHand; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static com.minecolonies.api.util.constant.translation.ToolTranslationConstants.*; /** * Permission scepter. used to add free to interact blocks or positions to the colonies permission list */ -public class ItemScepterPermission extends AbstractItemMinecolonies +public class ItemScepterPermission extends AbstractItemMinecolonies implements IBlockOverlayItem { /** * The NBT tag of the mode @@ -40,6 +55,10 @@ public class ItemScepterPermission extends AbstractItemMinecolonies */ private static final String TAG_VALUE_MODE_LOCATION = "modeLocation"; + private static final int GREEN_OVERLAY = 0xFF00FF00; + private static final int BLOCK_OVERLAY_RANGE_XZ = 32; + private static final int BLOCK_OVERLAY_RANGE_Y = 6; + /** * constructor. *

@@ -62,10 +81,13 @@ private static InteractionResult handleAddBlockType( final BlockState blockState = iColonyView.getWorld().getBlockState(pos); final Block block = blockState.getBlock(); + final ChangeFreeToInteractBlockMessage.MessageType type = Screen.hasControlDown() + ? ChangeFreeToInteractBlockMessage.MessageType.REMOVE_BLOCK + : ChangeFreeToInteractBlockMessage.MessageType.ADD_BLOCK; final ChangeFreeToInteractBlockMessage message = new ChangeFreeToInteractBlockMessage( iColonyView, block, - ChangeFreeToInteractBlockMessage.MessageType.ADD_BLOCK); + type); message.sendToServer(); return InteractionResult.SUCCESS; @@ -78,7 +100,10 @@ private static InteractionResult handleAddLocation( final BlockPos pos, final IColonyView iColonyView) { - final ChangeFreeToInteractBlockMessage message = new ChangeFreeToInteractBlockMessage(iColonyView, pos, ChangeFreeToInteractBlockMessage.MessageType.ADD_BLOCK); + final ChangeFreeToInteractBlockMessage.MessageType type = Screen.hasControlDown() + ? ChangeFreeToInteractBlockMessage.MessageType.REMOVE_BLOCK + : ChangeFreeToInteractBlockMessage.MessageType.ADD_BLOCK; + final ChangeFreeToInteractBlockMessage message = new ChangeFreeToInteractBlockMessage(iColonyView, pos, type); message.sendToServer(); return InteractionResult.SUCCESS; @@ -161,6 +186,63 @@ private static void toggleItemMode(final Player playerIn, final CompoundTag comp } } + @NotNull + @Override + public List getOverlayBoxes(@NotNull final Level world, @NotNull final Player player, @NotNull final ItemStack stack) + { + final List boxes = new ArrayList<>(); + final IColonyView colony = IColonyManager.getInstance().getClosestColonyView(world, player.blockPosition()); + if (colony == null || !colony.getPermissions().hasPermission(player, Action.EDIT_PERMISSIONS)) + { + return boxes; + } + + final String itemMode = stack.getOrCreateTag().getString(TAG_ITEM_MODE); + switch (itemMode) + { + case TAG_VALUE_MODE_BLOCK: + final Set freeBlocks = new HashSet<>(colony.getFreeBlocks()); + for (final BlockPos pos : BlockPos.withinManhattan(player.blockPosition(), BLOCK_OVERLAY_RANGE_XZ, BLOCK_OVERLAY_RANGE_Y, BLOCK_OVERLAY_RANGE_XZ)) + { + if (world.isLoaded(pos) && freeBlocks.contains(world.getBlockState(pos).getBlock())) + { + boxes.add(new OverlayBox(AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)), GREEN_OVERLAY, 0.02f, true)); + } + } + break; + case TAG_VALUE_MODE_LOCATION: + default: + for (final BlockPos pos : colony.getFreePositions()) + { + boxes.add(new OverlayBox(AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(pos)), GREEN_OVERLAY, 0.02f, true)); + } + break; + } + + return boxes; + } + + @Override + public void appendHoverText(@NotNull final ItemStack stack, @Nullable final Level level, + @NotNull final List tooltip, @NotNull final TooltipFlag flags) + { + final String itemMode = stack.getOrCreateTag().getString(TAG_ITEM_MODE); + final MutableComponent mode; + switch (itemMode) + { + case TAG_VALUE_MODE_BLOCK: + mode = Component.translatable(TOOL_PERMISSION_SCEPTER_MODE_BLOCK); + break; + case TAG_VALUE_MODE_LOCATION: + default: + mode = Component.translatable(TOOL_PERMISSION_SCEPTER_MODE_LOCATION); + break; + } + tooltip.add(Component.translatable(TOOL_PERMISSION_SCEPTER_MODE, mode.withStyle(ChatFormatting.YELLOW))); + + super.appendHoverText(stack, level, tooltip, flags); + } + @NotNull private static InteractionResult handleItemAction( final CompoundTag compound, diff --git a/src/main/java/com/minecolonies/core/network/messages/server/colony/ChangeFreeToInteractBlockMessage.java b/src/main/java/com/minecolonies/core/network/messages/server/colony/ChangeFreeToInteractBlockMessage.java index 59ccd892c30..ec82cecc7e6 100755 --- a/src/main/java/com/minecolonies/core/network/messages/server/colony/ChangeFreeToInteractBlockMessage.java +++ b/src/main/java/com/minecolonies/core/network/messages/server/colony/ChangeFreeToInteractBlockMessage.java @@ -106,8 +106,13 @@ protected Action permissionNeeded() @Override protected void onExecute(final PlayPayloadContext ctxIn, final ServerPlayer player, final IColony colony) { - //Verify player has permission to change this huts settings - if (msgType == MessageType.ADD_BLOCK) + final Player player = ctxIn.getSender(); + if (player == null) + { + return; + } + + if (type == MessageType.ADD_BLOCK) { switch (msgMode) { diff --git a/src/main/resources/assets/minecolonies/gui/townhall/layoutpermissions.xml b/src/main/resources/assets/minecolonies/gui/townhall/layoutpermissions.xml index 9681ad1b230..a33c2817ec0 100755 --- a/src/main/resources/assets/minecolonies/gui/townhall/layoutpermissions.xml +++ b/src/main/resources/assets/minecolonies/gui/townhall/layoutpermissions.xml @@ -5,16 +5,16 @@ -