Add stack-aware getAttributeModifiers and isSuitableFor to FabricItem ()

* Add stack-aware getAttributeModifiers to FabricItem

* Add stack-aware version of isSuitableFor

* Use the standard attack damage UUID
This commit is contained in:
Technici4n 2022-04-10 17:27:23 +02:00 committed by GitHub
parent 91896a4963
commit 4457765521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 11 deletions
fabric-item-api-v1/src
main
testmod/java/net/fabricmc/fabric/test/item

View file

@ -16,7 +16,13 @@
package net.fabricmc.fabric.api.item.v1;
import com.google.common.collect.Multimap;
import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@ -25,9 +31,7 @@ import net.minecraft.util.Hand;
/**
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
*
* <p>Note: This interface is automatically implemented on all items via Mixin,
* however it has to be implemented explicitly by modders to be able to override functions.
* In the future, it is planned that {@code public class Item implements FabricItem} will be visible in a development environment.
* <p>Note: This interface is automatically implemented on all items via Mixin and interface injection.
*
* <p>Note to maintainers: Functions should only be added to this interface if they are general-purpose enough,
* to be evaluated on a case-by-case basis. Otherwise they are better suited for more specialized APIs.
@ -61,4 +65,30 @@ public interface FabricItem {
default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return false;
}
/**
* Return the attribute modifiers to apply when this stack is worn in a living entity equipment slot.
* Stack-aware version of {@link Item#getAttributeModifiers(EquipmentSlot)}.
*
* <p>Note that attribute modifiers are only updated when the stack changes, i.e. when {@code ItemStack.areEqual(old, new)} is false.
*
* @param stack the current stack
* @param slot the equipment slot this stack is in
* @return the attribute modifiers
*/
default Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
return ((Item) this).getAttributeModifiers(slot);
}
/**
* Determines if mining with this item allows drops to be harvested from the specified block state.
* Stack-aware version of {@link Item#isSuitableFor(BlockState)}.
*
* @param stack the current stack
* @param state the block state of the targeted block
* @return true if drops can be harvested
*/
default boolean isSuitableFor(ItemStack stack, BlockState state) {
return ((Item) this).isSuitableFor(state);
}
}

View file

@ -18,15 +18,21 @@ package net.fabricmc.fabric.mixin.item;
import java.util.function.Consumer;
import com.google.common.collect.Multimap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@ -65,4 +71,26 @@ public abstract class ItemStackMixin {
this.fabric_damagingEntity = null;
this.fabric_breakCallback = null;
}
@Redirect(
method = "getAttributeModifiers",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/item/Item;getAttributeModifiers(Lnet/minecraft/entity/EquipmentSlot;)Lcom/google/common/collect/Multimap;"
)
)
public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) {
return item.getAttributeModifiers((ItemStack) (Object) this, slot);
}
@Redirect(
method = "isSuitableFor",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/item/Item;isSuitableFor(Lnet/minecraft/block/BlockState;)Z"
)
)
public boolean hookIsSuitableFor(Item item, BlockState state) {
return item.isSuitableFor((ItemStack) (Object) this, state);
}
}

View file

@ -27,8 +27,6 @@ import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.fabricmc.fabric.api.item.v1.FabricItem;
@Mixin(ClientPlayerInteractionManager.class)
public class ClientPlayerInteractionManagerMixin {
@Shadow
@ -58,7 +56,7 @@ public class ClientPlayerInteractionManagerMixin {
ItemStack oldStack = this.selectedStack;
ItemStack newStack = this.client.player.getMainHandStack();
if (oldStack.isOf(newStack.getItem()) && ((FabricItem) oldStack.getItem()).allowContinuingBlockBreaking(this.client.player, oldStack, newStack)) {
if (oldStack.isOf(newStack.getItem()) && oldStack.getItem().allowContinuingBlockBreaking(this.client.player, oldStack, newStack)) {
stackUnchanged = true;
}
}

View file

@ -51,7 +51,7 @@ public class HeldItemRendererMixin {
ItemStack newMainStack = client.player.getMainHandStack();
if (mainHand.getItem() == newMainStack.getItem()) {
if (!((FabricItem) mainHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.MAIN_HAND, mainHand, newMainStack)) {
if (!mainHand.getItem().allowNbtUpdateAnimation(client.player, Hand.MAIN_HAND, mainHand, newMainStack)) {
mainHand = newMainStack;
}
}
@ -60,7 +60,7 @@ public class HeldItemRendererMixin {
ItemStack newOffStack = client.player.getOffHandStack();
if (offHand.getItem() == newOffStack.getItem()) {
if (!((FabricItem) offHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.OFF_HAND, offHand, newOffStack)) {
if (!offHand.getItem().allowNbtUpdateAnimation(client.player, Hand.OFF_HAND, offHand, newOffStack)) {
offHand = newOffStack;
}
}

View file

@ -24,6 +24,9 @@
},
"description": "Hooks for items",
"custom": {
"fabric-api:module-lifecycle": "stable"
"fabric-api:module-lifecycle": "stable",
"loom:injected_interfaces": {
"net/minecraft/class_1792": ["net/fabricmc/fabric/api/item/v1/FabricItem"]
}
}
}

View file

@ -16,7 +16,15 @@
package net.fabricmc.fabric.test.item;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
@ -25,9 +33,10 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.item.v1.FabricItem;
public class UpdatingItem extends Item {
private static final EntityAttributeModifier PLUS_FIVE = new EntityAttributeModifier(
ATTACK_DAMAGE_MODIFIER_ID, "updating item", 5, EntityAttributeModifier.Operation.ADDITION);
public class UpdatingItem extends Item implements FabricItem {
private final boolean allowUpdateAnimation;
public UpdatingItem(boolean allowUpdateAnimation) {
@ -52,4 +61,30 @@ public class UpdatingItem extends Item implements FabricItem {
public boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return true; // set to false and you won't be able to break a block in survival with this item
}
// True for 15 seconds every 30 seconds
private boolean isEnabled(ItemStack stack) {
return !stack.hasNbt() || stack.getNbt().getLong("ticks") % 600 < 300;
}
@Override
public Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
// Give + 5 attack damage for 15 seconds every 30 seconds.
if (slot == EquipmentSlot.MAINHAND && isEnabled(stack)) {
return ImmutableMultimap.of(EntityAttributes.GENERIC_ATTACK_DAMAGE, PLUS_FIVE);
} else {
return ImmutableMultimap.of();
}
}
@Override
public boolean isSuitableFor(ItemStack stack, BlockState state) {
// Suitable for everything for 15 seconds every 30 seconds.
return isEnabled(stack);
}
@Override
public float getMiningSpeedMultiplier(ItemStack stack, BlockState state) {
return isEnabled(stack) ? 20 : super.getMiningSpeedMultiplier(stack, state);
}
}