Stack aware getFoodComponent (#3520)

* Stack aware `getFoodComponent`

Co-authored-by: Phoupraw <50520903+phoupraw@users.noreply.github.com>

* Back to redirects

---------

Co-authored-by: Phoupraw <50520903+phoupraw@users.noreply.github.com>
(cherry picked from commit d6f2b0841c)
This commit is contained in:
modmuss 2024-01-15 13:39:30 +00:00 committed by modmuss50
parent 92bc82c0df
commit fd154599a6
17 changed files with 511 additions and 3 deletions

View file

@ -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.registry.entry.RegistryEntry;
@ -123,4 +125,15 @@ public interface FabricItem {
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 <strong>recommended</strong> 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();
}
}

View file

@ -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));
}
}

View file

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

View file

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

View file

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

View file

@ -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<? extends AnimalEntity> 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();
}
}

View file

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

View file

@ -21,9 +21,16 @@ 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.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 @@ abstract class ItemMixin implements ItemExtensions, FabricItem {
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();
}
}

View file

@ -28,6 +28,7 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EquipmentSlot;
@ -101,4 +102,9 @@ public abstract class ItemStackMixin implements FabricItemStack {
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<Boolean> cir) {
cir.setReturnValue(this.getFoodComponent() != null);
}
}

View file

@ -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<EquipmentSlot> info) {
EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) stack.getItem()).fabric_getEquipmentSlotProvider();
@ -38,4 +47,19 @@ abstract class LivingEntityMixin {
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();
}
}

View file

@ -0,0 +1,57 @@
/*
* 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 com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
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 {
@Inject(method = "interactMob", at = @At("HEAD"))
private void storeCopy(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> cir, @Share("interaction_stack") LocalRef<ItemStack> stackRef) {
stackRef.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, @Share("interaction_stack") LocalRef<ItemStack> stackRef) {
return stackRef.get().getFoodComponent();
}
@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();
}
}

View file

@ -5,11 +5,17 @@
"mixins": [
"AbstractFurnaceBlockEntityMixin",
"ArmorItemMixin",
"BlockItemMixin",
"BrewingStandBlockEntityMixin",
"CatEntityMixin",
"CookingRecipeJsonBuilderMixin",
"FoxEntityMixin",
"HungerManagerMixin",
"ItemMixin",
"ItemStackMixin",
"LivingEntityMixin",
"RecipeMixin"
"RecipeMixin",
"WolfEntityMixin"
],
"injectors": {
"defaultRequire": 1

View file

@ -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);
}
}
}

View file

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

View file

@ -0,0 +1,3 @@
{
"parent": "minecraft:item/wooden_pickaxe"
}

View file

@ -0,0 +1,3 @@
{
"parent": "minecraft:item/book"
}

View file

@ -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"
]
}
}