From 94c864759d8ad98abaadc6c053edbc12e420af3d Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 29 Apr 2024 21:02:14 +0100 Subject: [PATCH] Ignore enchantments/attributes on the original item Turtle tools were not equippable, as we considered the stack enchanted due to the item's base attribute modifiers. We now only check the component patch for enchantments/attribute modifiers. This also removes the craftItem property of tools - this hasn't worked since we added support for enchanted tools! Fixes #1810 --- .../api/turtle/TurtleUpgradeDataProvider.java | 23 +++---------- .../impl/upgrades/TurtleToolSpec.java | 9 ++--- .../client/turtle/TurtleModemModeller.java | 4 +-- .../shared/turtle/upgrades/TurtleTool.java | 34 ++++++------------- .../shared/util/DataComponentUtil.java | 16 +++++++++ 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java index b403c54bd..96c648087 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java @@ -58,7 +58,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider> add) { upgrade(id, ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec( adjective, - Optional.ofNullable(craftingItem), - toolItem, + item, damageMultiplier, allowEnchantments, consumeDurability, diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java index 60935d5a9..de2db697a 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java @@ -22,8 +22,7 @@ import java.util.Optional; * The template for a turtle tool. * * @param adjective The adjective for this tool. - * @param craftItem The item used to craft this tool. - * @param toolItem The actual tool used. + * @param item The tool used. * @param damageMultiplier The damage multiplier for this tool. * @param allowEnchantments Whether to allow enchantments. * @param consumeDurability When to consume durability. @@ -31,8 +30,7 @@ import java.util.Optional; */ public record TurtleToolSpec( Component adjective, - Optional craftItem, - Item toolItem, + Item item, float damageMultiplier, boolean allowEnchantments, TurtleToolDurability consumeDurability, @@ -42,8 +40,7 @@ public record TurtleToolSpec( public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( ComponentSerialization.CODEC.fieldOf("adjective").forGetter(TurtleToolSpec::adjective), - BuiltInRegistries.ITEM.byNameCodec().optionalFieldOf("craftingItem").forGetter(TurtleToolSpec::craftItem), - BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::toolItem), + BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::item), Codec.FLOAT.optionalFieldOf("damageMultiplier", DEFAULT_DAMAGE_MULTIPLIER).forGetter(TurtleToolSpec::damageMultiplier), Codec.BOOL.optionalFieldOf("allowEnchantments", false).forGetter(TurtleToolSpec::allowEnchantments), TurtleToolDurability.CODEC.optionalFieldOf("consumeDurability", TurtleToolDurability.NEVER).forGetter(TurtleToolSpec::consumeDurability), diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java index cf775240c..29367c358 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java +++ b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleModemModeller.java @@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.turtle.upgrades.TurtleModem; +import dan200.computercraft.shared.util.DataComponentUtil; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; @@ -23,8 +24,7 @@ import java.util.stream.Stream; public class TurtleModemModeller implements TurtleUpgradeModeller { @Override public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) { - var component = data.get(ModRegistry.DataComponents.ON.get()); - var active = component != null && component.isPresent() && component.get(); + var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x); var models = upgrade.advanced() ? ModemModels.ADVANCED : ModemModels.NORMAL; return side == TurtleSide.LEFT diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java index 6e49b5292..e29d85335 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -14,6 +14,7 @@ import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.turtle.TurtleUtil; import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; import dan200.computercraft.shared.turtle.core.TurtlePlayer; +import dan200.computercraft.shared.util.DataComponentUtil; import dan200.computercraft.shared.util.DropConsumer; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.core.BlockPos; @@ -49,37 +50,26 @@ public class TurtleTool extends AbstractTurtleUpgrade { private static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure("Cannot break block with this tool"); final TurtleToolSpec spec; - final ItemStack item; - final float damageMulitiplier; - final boolean allowEnchantments; - final TurtleToolDurability consumeDurability; final @Nullable TagKey breakable; public TurtleTool(TurtleToolSpec spec) { - super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.craftItem().orElse(spec.toolItem()))); + super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.item())); this.spec = spec; - item = new ItemStack(spec.toolItem()); - this.damageMulitiplier = spec.damageMultiplier(); - this.allowEnchantments = spec.allowEnchantments(); - this.consumeDurability = spec.consumeDurability(); this.breakable = spec.breakable().orElse(null); } @Override public boolean isItemSuitable(ItemStack stack) { - if (consumeDurability == TurtleToolDurability.NEVER && stack.isDamaged()) return false; - if (!allowEnchantments && isEnchanted(stack)) return false; + if (spec.consumeDurability() == TurtleToolDurability.NEVER && stack.isDamaged()) return false; + if (!spec.allowEnchantments() && isEnchanted(stack)) return false; return true; } private static boolean isEnchanted(ItemStack stack) { - var enchantments = stack.get(DataComponents.ENCHANTMENTS); - if (enchantments != null && !enchantments.isEmpty()) return true; - - var modifiers = stack.get(DataComponents.ATTRIBUTE_MODIFIERS); - if (modifiers != null && !modifiers.modifiers().isEmpty()) return true; - - return false; + // Only check whether the stack has been modified. We ignore components on the original item. + var patch = stack.getComponentsPatch(); + return DataComponentUtil.isPresent(patch, DataComponents.ENCHANTMENTS, x -> !x.isEmpty()) + || DataComponentUtil.isPresent(patch, DataComponents.ATTRIBUTE_MODIFIERS, x -> !x.modifiers().isEmpty()); } @Override @@ -100,9 +90,7 @@ public class TurtleTool extends AbstractTurtleUpgrade { } private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack oldStack, ItemStack stack) { - var upgradeData = turtle.getUpgradeData(side); - - var useDurability = switch (consumeDurability) { + var useDurability = switch (spec.consumeDurability()) { case NEVER -> false; case WHEN_ENCHANTED -> isEnchanted(oldStack); case ALWAYS -> true; @@ -116,7 +104,7 @@ public class TurtleTool extends AbstractTurtleUpgrade { } // If the tool has changed, no clue what's going on. - if (stack.getItem() != item.getItem()) return; + if (stack.getItem() != spec.item()) return; turtle.setUpgradeData(side, stack.getComponentsPatch()); } @@ -218,7 +206,7 @@ public class TurtleTool extends AbstractTurtleUpgrade { * @see Player#attack(Entity) */ private boolean attack(ServerPlayer player, Direction direction, Entity entity) { - var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * damageMulitiplier; + var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * spec.damageMultiplier(); var bonusDamage = EnchantmentHelper.getDamageBonus(player.getItemInHand(InteractionHand.MAIN_HAND), entity.getType()); var damage = baseDamage + bonusDamage; if (damage <= 0) return false; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java b/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java index 163e08541..7ec99eeca 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/DataComponentUtil.java @@ -5,6 +5,7 @@ package dan200.computercraft.shared.util; import net.minecraft.core.component.DataComponentHolder; +import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; @@ -13,6 +14,7 @@ import net.minecraft.world.level.ItemLike; import org.jetbrains.annotations.Contract; import javax.annotation.Nullable; +import java.util.function.Predicate; /** * Utilities for working with {@linkplain DataComponentType data components}. @@ -43,4 +45,18 @@ public class DataComponentUtil { public static ItemStack createStack(ItemLike item, DataComponentType type, @Nullable T value) { return set(new ItemStack(item), type, value); } + + /** + * Check a component is present in a {@link DataComponentPatch} and matches the supplied predicate. + * + * @param patch The current component patch. + * @param component The component type. + * @param check The predicate to check against. + * @param The type of component. + * @return Whether the component is present in this patch, and matches the supplied predicate. + */ + public static boolean isPresent(DataComponentPatch patch, DataComponentType component, Predicate check) { + var value = patch.get(component); + return value != null && value.isPresent() && check.test(value.get()); + } }