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()); + } }