diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java index e6f129f9b8..5be9d9c3a4 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java @@ -17,12 +17,14 @@ package net.fabricmc.fabric.api.item.v1; import com.google.common.collect.Multimap; +import org.jetbrains.annotations.Nullable; import net.minecraft.block.BlockState; 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.FoodComponent; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; @@ -122,4 +124,15 @@ default boolean isSuitableFor(ItemStack stack, BlockState state) { default ItemStack getRecipeRemainder(ItemStack stack) { return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY; } + + /** + * This is a stack-aware version of {@link Item#getFoodComponent()}. + * Note that simple food component can also be set via {@link Item.Settings#food(FoodComponent)}. + * If you want to get a food component for a stack, is recommended to use the stack version of this method: {@link FabricItemStack#getFoodComponent()}. + * + * @return this item's {@link FoodComponent}, or {@code null} if none was set + */ + default @Nullable FoodComponent getFoodComponent(ItemStack stack) { + return ((Item) this).getFoodComponent(); + } } diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java index 0c646a6aad..4f1932ca00 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java @@ -16,6 +16,9 @@ package net.fabricmc.fabric.api.item.v1; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.item.FoodComponent; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -36,4 +39,14 @@ public interface FabricItemStack { default ItemStack getRecipeRemainder() { return ((ItemStack) this).getItem().getRecipeRemainder((ItemStack) this); } + + /** + * Stack-aware version of {@link Item#getFoodComponent()}. + * See {@link FabricItem#getFoodComponent(ItemStack)} for a more in depth description. + * + * @return this item stack's {@link FoodComponent}, or {@code null} if none was set + */ + default @Nullable FoodComponent getFoodComponent() { + return ((ItemStack) this).getItem().getFoodComponent(((ItemStack) this)); + } } diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java new file mode 100644 index 0000000000..bef3b5e09a --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemUsageContext; + +@Mixin(BlockItem.class) +class BlockItemMixin { + @Redirect(method = "useOnBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;isFood()Z")) + private boolean isStackAwareFood(BlockItem instance, ItemUsageContext context) { + return context.getStack().isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java new file mode 100644 index 0000000000..45040e85a9 --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.passive.CatEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.util.Hand; + +@Mixin(CatEntity.class) +class CatEntityMixin { + @Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, PlayerEntity player, Hand hand) { + return player.getStackInHand(hand).getFoodComponent(); + } + + @Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, PlayerEntity player, Hand hand) { + return player.getStackInHand(hand).isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java new file mode 100644 index 0000000000..ebb69882ed --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.data.server.recipe.CookingRecipeJsonBuilder; +import net.minecraft.item.Item; + +@Mixin(CookingRecipeJsonBuilder.class) +class CookingRecipeJsonBuilderMixin { + @Redirect(method = "getSmeltingRecipeCategory", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private static boolean isStackAwareFood(Item instance) { + return instance.getDefaultStack().isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java new file mode 100644 index 0000000000..c8a7ea63ef --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.EntityType; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.FoxEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +@Mixin(FoxEntity.class) +abstract class FoxEntityMixin extends AnimalEntity { + protected FoxEntityMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Redirect(method = {"canEat", "canPickupItem"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z", ordinal = 0)) + private boolean isStackAwareFood(Item instance, ItemStack stack) { + return stack.isFood(); + } + + @Redirect(method = {"canPickupItem"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z", ordinal = 1)) + private boolean isEquippedStackAwareFood(Item instance) { + return this.getEquippedStack(EquipmentSlot.MAINHAND).isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java new file mode 100644 index 0000000000..39836fad32 --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.player.HungerManager; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +@Mixin(HungerManager.class) +class HungerManagerMixin { + @Redirect(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, Item item, ItemStack stack) { + return stack.getFoodComponent(); + } + + @Redirect(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, Item item, ItemStack stack) { + return stack.isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java index 8214295ee3..8d18b0b434 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java @@ -21,9 +21,16 @@ 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FoodComponent; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.world.World; import net.fabricmc.fabric.api.item.v1.CustomDamageHandler; import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider; @@ -67,4 +74,29 @@ public CustomDamageHandler fabric_getCustomDamageHandler() { public void fabric_setCustomDamageHandler(@Nullable CustomDamageHandler handler) { this.customDamageHandler = handler; } + + @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, World world, PlayerEntity user, Hand hand) { + return user.getStackInHand(hand).getFoodComponent(); + } + + @Redirect(method = "getMaxUseTime", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack) { + return stack.getFoodComponent(); + } + + @Redirect(method = {"getMaxUseTime", "getUseAction"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, ItemStack stack) { + return stack.isFood(); + } + + @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, World world, PlayerEntity user, Hand hand) { + return user.getStackInHand(hand).isFood(); + } + + @Redirect(method = "finishUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, ItemStack stack, World world, LivingEntity user) { + return stack.isFood(); + } } diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java index 89df9b5c81..0d311f3c33 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java @@ -28,6 +28,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.block.BlockState; import net.minecraft.entity.EquipmentSlot; @@ -44,7 +45,8 @@ @Mixin(ItemStack.class) public abstract class ItemStackMixin implements FabricItemStack { - @Shadow public abstract Item getItem(); + @Shadow + public abstract Item getItem(); @Unique private LivingEntity fabric_damagingEntity; @@ -100,4 +102,9 @@ public Multimap hookGetAttributeModifi public boolean hookIsSuitableFor(Item item, BlockState state) { return item.isSuitableFor((ItemStack) (Object) this, state); } + + @Inject(method = "isFood", at = @At("HEAD"), cancellable = true) + public void isStackAwareFood(CallbackInfoReturnable cir) { + cir.setReturnValue(this.getFoodComponent() != null); + } } diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java index d147509d47..920468203a 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java @@ -16,20 +16,29 @@ package net.fabricmc.fabric.mixin.item; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.LivingEntity; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.world.World; import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider; import net.fabricmc.fabric.impl.item.ItemExtensions; @Mixin(LivingEntity.class) abstract class LivingEntityMixin { + @Shadow + protected ItemStack activeItemStack; + @Inject(method = "getPreferredEquipmentSlot", at = @At(value = "HEAD"), cancellable = true) private static void onGetPreferredEquipmentSlot(ItemStack stack, CallbackInfoReturnable info) { EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) stack.getItem()).fabric_getEquipmentSlotProvider(); @@ -38,4 +47,19 @@ private static void onGetPreferredEquipmentSlot(ItemStack stack, CallbackInfoRet info.setReturnValue(equipmentSlotProvider.getPreferredEquipmentSlot(stack)); } } + + @Redirect(method = "shouldSpawnConsumptionEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance) { + return this.activeItemStack.getFoodComponent(); + } + + @Redirect(method = "applyFoodEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack, World world, LivingEntity targetEntity) { + return stack.getFoodComponent(); + } + + @Redirect(method = "applyFoodEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z")) + private boolean isStackAwareFood(Item instance, ItemStack stack, World world, LivingEntity targetEntity) { + return stack.isFood(); + } } diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java new file mode 100644 index 0000000000..ca3245ee09 --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.item; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.entity.passive.WolfEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; + +@Mixin(WolfEntity.class) +class WolfEntityMixin { + @Unique + private static final ThreadLocal INTERACTION_STACK = new ThreadLocal<>(); + + @Inject(method = "interactMob", at = @At("HEAD")) + private void storeCopy(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + INTERACTION_STACK.set(player.getStackInHand(hand).copy()); + } + + @Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, PlayerEntity player, Hand hand) { + FoodComponent fc = INTERACTION_STACK.get().getFoodComponent(); + INTERACTION_STACK.remove(); + return fc; + } + + @Redirect(method = "isBreedingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;")) + private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack) { + return stack.getFoodComponent(); + } + + @Redirect(method = "isBreedingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z", ordinal = 0)) + private boolean isStackAwareFood(Item instance, ItemStack stack) { + return stack.isFood(); + } +} diff --git a/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json b/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json index 8993bf8b2b..59dae72028 100644 --- a/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json +++ b/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json @@ -5,11 +5,17 @@ "mixins": [ "AbstractFurnaceBlockEntityMixin", "ArmorItemMixin", + "BlockItemMixin", "BrewingStandBlockEntityMixin", + "CatEntityMixin", + "CookingRecipeJsonBuilderMixin", + "FoxEntityMixin", + "HungerManagerMixin", "ItemMixin", "ItemStackMixin", "LivingEntityMixin", - "RecipeMixin" + "RecipeMixin", + "WolfEntityMixin" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java index 932259c3d1..fcd6768ee9 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java @@ -22,10 +22,10 @@ import net.minecraft.item.ToolMaterials; import net.minecraft.nbt.NbtCompound; import net.minecraft.potion.Potions; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.item.v1.CustomDamageHandler; diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java new file mode 100644 index 0000000000..4d58c04dec --- /dev/null +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.item; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.item.FoodComponent; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.item.v1.FabricItemSettings; + +public final class FoodGameInitializer implements ModInitializer { + public static final Item DAMAGE = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "damage_food"), new DamageFood(new FabricItemSettings().maxDamage(20))); + public static final Item NAME = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "name_food"), new NameFood(new FabricItemSettings())); + + @Override + public void onInitialize() { + } + + public static class DamageFood extends Item { + public DamageFood(Settings settings) { + super(settings); + } + + @Override + public @Nullable FoodComponent getFoodComponent(ItemStack stack) { + return new FoodComponent.Builder() + .hunger(20 - 20 * stack.getDamage() / stack.getMaxDamage()) + .saturationModifier(0.5f) + .build(); + } + } + + public static class NameFood extends Item { + public NameFood(Settings settings) { + super(settings); + } + + @Override + public @Nullable FoodComponent getFoodComponent(ItemStack stack) { + return Registries.ITEM.get(new Identifier(stack.getName().getString())).getFoodComponent(stack); + } + } +} diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java new file mode 100644 index 0000000000..16938db7d5 --- /dev/null +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.item.gametest; + +import java.util.Objects; + +import net.minecraft.entity.EntityType; +import net.minecraft.entity.passive.WolfEntity; +import net.minecraft.entity.player.HungerManager; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FoodComponent; +import net.minecraft.item.FoodComponents; +import net.minecraft.item.ItemStack; +import net.minecraft.test.GameTest; +import net.minecraft.test.TestContext; +import net.minecraft.text.Text; +import net.minecraft.util.Hand; +import net.minecraft.util.math.Vec3d; + +import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; +import net.fabricmc.fabric.test.item.FoodGameInitializer; + +public final class FoodGameTest implements FabricGameTest { + @GameTest(templateName = EMPTY_STRUCTURE) + public void damageFoodTest(TestContext context) { + PlayerEntity player = context.createMockSurvivalPlayer(); + HungerManager hungerManager = player.getHungerManager(); + + for (int damage : new int[]{0, 1, 10, 19}) { + hungerManager.setFoodLevel(0); + hungerManager.setSaturationLevel(0); + ItemStack foodStack = FoodGameInitializer.DAMAGE.getDefaultStack(); + foodStack.setDamage(damage); + player.eatFood(player.getWorld(), foodStack.copy()); + FoodComponent fc = Objects.requireNonNull(foodStack.getFoodComponent()); + int foodActual = hungerManager.getFoodLevel(); + int foodExpect = Math.min(20, fc.getHunger()); + context.assertTrue(foodActual == foodExpect, "damage=%d, food actual %d, expect %d".formatted(damage, foodActual, foodExpect)); + float satActual = hungerManager.getSaturationLevel(); + float satExpect = Math.min(foodExpect, fc.getHunger() * fc.getSaturationModifier() * 2); + context.assertTrue(satActual == satExpect, "damage=%d, sat actual %f, expect %f".formatted(damage, satActual, satExpect)); + } + + context.complete(); + } + + @GameTest(templateName = EMPTY_STRUCTURE) + public void nameFoodTest(TestContext context) { + PlayerEntity player = context.createMockSurvivalPlayer(); + HungerManager hungerManager = player.getHungerManager(); + hungerManager.setFoodLevel(0); + hungerManager.setSaturationLevel(0); + ItemStack foodStack = FoodGameInitializer.NAME.getDefaultStack(); + foodStack.setCustomName(Text.literal("enchanted_golden_apple")); + player.eatFood(player.getWorld(), foodStack.copy()); + FoodComponent fc = FoodComponents.ENCHANTED_GOLDEN_APPLE; + int foodActual = hungerManager.getFoodLevel(); + int foodExpect = Math.min(20, fc.getHunger()); + context.assertTrue(foodActual == foodExpect, "enchanted_golden_apple, food actual %d, expect %d".formatted(foodActual, foodExpect)); + float satActual = hungerManager.getSaturationLevel(); + float satExpect = Math.min(foodExpect, fc.getHunger() * fc.getSaturationModifier() * 2); + context.assertTrue(satActual == satExpect, "enchanted_golden_apple, sat actual %f, expect %f".formatted(satActual, satExpect)); + context.complete(); + } + + @GameTest(templateName = EMPTY_STRUCTURE) + public void nameMeatTest(TestContext context) { + PlayerEntity player = context.createMockSurvivalPlayer(); + WolfEntity wolf = context.spawnEntity(EntityType.WOLF, context.getRelative(Vec3d.ZERO)); + wolf.setTamed(true); + wolf.setOwner(player); + wolf.setHealth(1f); + ItemStack meat = FoodGameInitializer.NAME.getDefaultStack(); + meat.setCustomName(Text.of("mutton")); + player.setStackInHand(Hand.MAIN_HAND, meat); + player.interact(wolf, Hand.MAIN_HAND); + float wolfHealth = wolf.getHealth(); + context.assertTrue(wolfHealth > 0, "actual %f, expect > 0".formatted(wolfHealth)); + context.complete(); + } +} diff --git a/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json new file mode 100644 index 0000000000..a254098776 --- /dev/null +++ b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/wooden_pickaxe" +} diff --git a/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json new file mode 100644 index 0000000000..70e896e705 --- /dev/null +++ b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/book" +} diff --git a/fabric-item-api-v1/src/testmod/resources/fabric.mod.json b/fabric-item-api-v1/src/testmod/resources/fabric.mod.json index 56e2f60a6d..627b046066 100644 --- a/fabric-item-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-item-api-v1/src/testmod/resources/fabric.mod.json @@ -14,7 +14,8 @@ "net.fabricmc.fabric.test.item.FabricItemSettingsTests", "net.fabricmc.fabric.test.item.ItemUpdateAnimationTest", "net.fabricmc.fabric.test.item.ModifyItemAttributeModifiersCallbackTest", - "net.fabricmc.fabric.test.item.ArmorKnockbackResistanceTest" + "net.fabricmc.fabric.test.item.ArmorKnockbackResistanceTest", + "net.fabricmc.fabric.test.item.FoodGameInitializer" ], "client": [ "net.fabricmc.fabric.test.item.client.TooltipTests" @@ -22,7 +23,8 @@ "fabric-gametest" : [ "net.fabricmc.fabric.test.item.gametest.BrewingStandGameTest", "net.fabricmc.fabric.test.item.gametest.FurnaceGameTest", - "net.fabricmc.fabric.test.item.gametest.RecipeGameTest" + "net.fabricmc.fabric.test.item.gametest.RecipeGameTest", + "net.fabricmc.fabric.test.item.gametest.FoodGameTest" ] }, "mixins": [